diff --git a/core/pom.xml b/core/pom.xml index 64a77386a..1a8aebcd0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -145,6 +145,11 @@ + + org.junit.jupiter + junit-jupiter-engine + test + io.hosuaby inject-resources-junit-jupiter diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/DefaultConfiguration.java b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/DefaultConfiguration.java index 1e32fb11a..a806bcbfe 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/DefaultConfiguration.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/DefaultConfiguration.java @@ -34,8 +34,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * Provides a basic configuration management implementation that allows mutable access to - * configuration state. + * Provides a basic configuration management implementation that allows mutable + * access to configuration state. * * @param * the type of managed features @@ -112,16 +112,6 @@ public IMutableConfiguration applyConfiguration(@NonNull IConfiguration or return this; } - @SuppressWarnings("unchecked") - @Override - public V get(T feature) { - V value = (V) featureValues.get(feature); - if (value == null) { - value = (V) feature.getDefault(); - } - return value; - } - @Override public IMutableConfiguration set(T feature, Object value) { Class featureValueClass = feature.getValueClass(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfiguration.java b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfiguration.java index 44abc0027..31f8e2bd8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfiguration.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfiguration.java @@ -31,8 +31,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * The base interface for getting the configuration of processors and parsers in this library. This - * provides an immutable view of the current configuration. + * The base interface for getting the configuration of processors and parsers in + * this library. This provides an immutable view of the current configuration. * * @param * the type of the feature set @@ -59,8 +59,15 @@ public interface IConfiguration> { * the requested feature * @return the value of the feature */ + @SuppressWarnings("unchecked") @NonNull - V get(@NonNull T feature); + default V get(@NonNull T feature) { + V value = (V) getFeatureValues().get(feature); + if (value == null) { + value = (V) feature.getDefault(); + } + return value; + } /** * Get the mapping of each feature mapped to its value. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfigurationFeature.java b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfigurationFeature.java index 80d6c6523..cb3522ce3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfigurationFeature.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/configuration/IConfigurationFeature.java @@ -32,7 +32,8 @@ * The common interface that all configuration features must implement. *

* This approach is inspired by the configuration implementation in the - * Jackson databind library. + * Jackson databind + * library. * * @param * the value type of the feature diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java index 70180978c..75105a67a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java @@ -32,14 +32,15 @@ import gov.nist.secauto.metaschema.core.metapath.function.DefaultFunction.CallingContext; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.model.IUriResolver; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - import java.io.IOException; +import java.io.InputStream; import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; import java.time.Clock; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -49,7 +50,6 @@ import java.util.concurrent.ConcurrentHashMap; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; public class DynamicContext { // NOPMD - intentional data class @NonNull @@ -131,6 +131,19 @@ public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequen assert old == null; } + @NonNull + public ISequence getVariableValue(String name) { + return ObjectUtils.requireNonNull(letVariableMap.get(name)); + } + + public void setVariableValue(String name, ISequence boundValue) { + letVariableMap.put(name, boundValue); + } + + public void clearVariableValue(String name) { + letVariableMap.remove(name); + } + private class CachingLoader implements IDocumentLoader { @NonNull private final IDocumentLoader proxy; @@ -140,86 +153,83 @@ public CachingLoader(@NonNull IDocumentLoader proxy) { } @Override - public @Nullable EntityResolver getEntityResolver() { - return new ContextEntityResolver(); + public IUriResolver getUriResolver() { + return new ContextUriResolver(); } @Override - public void setEntityResolver(@NonNull EntityResolver resolver) { + public void setUriResolver(@NonNull IUriResolver resolver) { // we delegate to the document loader proxy, so the resolver should be set there throw new UnsupportedOperationException("Set the resolver on the proxy"); } + @NonNull protected IDocumentLoader getProxiedDocumentLoader() { return proxy; } @Override - public @NonNull IDocumentNodeItem loadAsNodeItem(@NonNull InputSource source) throws IOException { - String systemId = source.getSystemId(); - URI uri = ObjectUtils.notNull(URI.create(systemId)); + public IDocumentNodeItem loadAsNodeItem(Path path) throws IOException { + URI uri = path.toUri(); + IDocumentNodeItem retval = availableDocuments.get(uri); + if (retval == null) { + retval = getProxiedDocumentLoader().loadAsNodeItem(path); + availableDocuments.put(uri, retval); + } + return retval; + } + + @Override + public IDocumentNodeItem loadAsNodeItem(URL url) throws IOException, URISyntaxException { + URI uri = ObjectUtils.notNull(url.toURI()); + IDocumentNodeItem retval = availableDocuments.get(uri); + if (retval == null) { + retval = getProxiedDocumentLoader().loadAsNodeItem(uri); + availableDocuments.put(uri, retval); + } + return retval; + } + + @Override + public IDocumentNodeItem loadAsNodeItem(URI uri) throws IOException { IDocumentNodeItem retval = availableDocuments.get(uri); if (retval == null) { - retval = getProxiedDocumentLoader().loadAsNodeItem(source); + retval = getProxiedDocumentLoader().loadAsNodeItem(uri); availableDocuments.put(uri, retval); } return retval; } - public class ContextEntityResolver implements EntityResolver { + @Override + public @NonNull IDocumentNodeItem loadAsNodeItem( + @NonNull InputStream is, + @NonNull URI documentUri) throws IOException { + throw new UnsupportedOperationException(); + // return getProxiedDocumentLoader().loadAsNodeItem(is, documentUri); + } + + public class ContextUriResolver implements IUriResolver { /** - * Provides an {@link InputSource} for the provided {@code systemId} after attempting to resolve - * this system identifier. + * {@inheritDoc} *

- * This implementation of an {@link EntityResolver} will perform the following operations in order: - *

    - *
  1. Resolves the {@code systemId} against the base URI provided by the - * {@link StaticContext#getBaseUri()} method, if this method returns a non-{@code null} result, to - * get a localized resource identifier.
  2. - *
  3. It will then delegate to the EntityResolver provided by the - * {@link IDocumentLoader#getEntityResolver()} method, if the result is not-{@code null}, to get the - * {@link InputSource}.
  4. - *
  5. If no InputSource is provided by the previous step, then an InputSource will be created from - * the URI resolved in the first step, if possible. - *
  6. If an InputSource is still not provided, then an InputSource will be created from the - * provided {@code systemId}. - *
+ * This method first resolves the provided URI against the static context's base + * URI. */ @Override - public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + public URI resolve(URI uri) { URI baseUri = getStaticContext().getBaseUri(); - String uri; + URI resolvedUri; if (baseUri == null) { - uri = systemId; + resolvedUri = uri; } else { - URI resolvedUri = baseUri.resolve(systemId); - uri = resolvedUri.toASCIIString(); - } - - EntityResolver resolver = getProxiedDocumentLoader().getEntityResolver(); - InputSource retval = resolver == null ? null : resolver.resolveEntity(null, uri); - if (retval == null) { - retval = new InputSource(uri); + resolvedUri = ObjectUtils.notNull(baseUri.resolve(uri)); } - return retval; + IUriResolver resolver = getProxiedDocumentLoader().getUriResolver(); + return resolver == null ? resolvedUri : resolver.resolve(resolvedUri); } - } } - - @NonNull - public ISequence getVariableValue(String name) { - return ObjectUtils.requireNonNull(letVariableMap.get(name)); - } - - public void setVariableValue(String name, ISequence boundValue) { - letVariableMap.put(name, boundValue); - } - - public void clearVariableValue(String name) { - letVariableMap.remove(name); - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IDocumentLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IDocumentLoader.java index 572cf8f94..1bbd4e324 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IDocumentLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IDocumentLoader.java @@ -28,47 +28,43 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.model.IResourceLoader; +import gov.nist.secauto.metaschema.core.model.IUriResolver; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import edu.umd.cs.findbugs.annotations.NonNull; public interface IDocumentLoader extends IResourceLoader { - void setEntityResolver(@NonNull EntityResolver resolver); + void setUriResolver(@NonNull IUriResolver resolver); @NonNull - default IDocumentNodeItem loadAsNodeItem(@NonNull URL url) throws IOException, URISyntaxException { - return loadAsNodeItem(toInputSource(ObjectUtils.notNull(url.toURI()))); + default IDocumentNodeItem loadAsNodeItem(@NonNull File file) throws IOException { + return loadAsNodeItem(ObjectUtils.notNull(file.toPath())); } @NonNull default IDocumentNodeItem loadAsNodeItem(@NonNull Path path) throws IOException { - return loadAsNodeItem(toInputSource(ObjectUtils.notNull(path.toUri()))); + try (InputStream is = ObjectUtils.notNull(Files.newInputStream(path))) { + return loadAsNodeItem(is, ObjectUtils.notNull(path.toUri())); + } } @NonNull - default IDocumentNodeItem loadAsNodeItem(@NonNull File file) throws IOException { - return loadAsNodeItem(toInputSource(ObjectUtils.notNull(file.toPath().toUri()))); + default IDocumentNodeItem loadAsNodeItem(@NonNull URL url) throws IOException, URISyntaxException { + return loadAsNodeItem(ObjectUtils.notNull(url.toURI())); } @NonNull - default IDocumentNodeItem loadAsNodeItem(@NonNull InputStream is, @NonNull URI documentUri) throws IOException { - InputSource source = toInputSource(documentUri); - source.setByteStream(is); - // TODO: deal with charset? - return loadAsNodeItem(source); - } + IDocumentNodeItem loadAsNodeItem(@NonNull URI uri) throws IOException; @NonNull - IDocumentNodeItem loadAsNodeItem(@NonNull InputSource source) throws IOException; + IDocumentNodeItem loadAsNodeItem(@NonNull InputStream is, @NonNull URI documentUri) throws IOException; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java index 4695ea56f..3aa6c9515 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.util.List; import edu.umd.cs.findbugs.annotations.NonNull; @@ -82,10 +81,11 @@ private static ISequence execute(@NonNull IFunction function, } /** - * Dynamically load the document associated with the URI, and return a {@link IDocumentNodeItem} - * containing the result. + * Dynamically load the document associated with the URI, and return a + * {@link IDocumentNodeItem} containing the result. *

- * Based on the XPath 3.1 fn:doc + * Based on the XPath 3.1 + * fn:doc * function. * * @param uri @@ -115,8 +115,8 @@ public static IDocumentNodeItem fnDoc(@NonNull IStringItem uri, @NonNull Dynamic } try { - return context.getDocumentLoader().loadAsNodeItem(ObjectUtils.notNull(documentUri.toURL())); - } catch (IOException | URISyntaxException ex) { + return context.getDocumentLoader().loadAsNodeItem(ObjectUtils.notNull(documentUri)); + } catch (IOException ex) { throw new DocumentFunctionException(DocumentFunctionException.ERROR_RETRIEVING_RESOURCE, String .format("Unable to retrieve the resource identified by the URI '%s'.", documentUri.toString()), ex); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java index 920910605..b1ea60637 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java @@ -32,12 +32,13 @@ public interface IItem { /** * Get the item's "wrapped" value. This "wrapped" value may be: *

    - *
  • In the case of an Assembly, a Java object representing the fields and flags of the - * assembly.
  • - *
  • In the case of a Field with flags, a Java object representing the field value and flags of - * the field. + *
  • In the case of an Assembly, a Java object representing the fields and + * flags of the assembly.
  • + *
  • In the case of a Field with flags, a Java object representing the field + * value and flags of the field. *
  • In the case of a Field without flags or a flag, a Java type managed by a - * {@link IDataTypeAdapter} or a primitive type provided by the Java standard library. + * {@link IDataTypeAdapter} or a primitive type provided by the Java standard + * library. *
* * @return the value or {@code null} if the item has no available value @@ -47,7 +48,8 @@ public interface IItem { /** * Determine if the item has an associated value. * - * @return {@code true} if the item has a non-{@code null} value or {@code false} otherwise + * @return {@code true} if the item has a non-{@code null} value or + * {@code false} otherwise */ default boolean hasValue() { return getValue() != null; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java index 797dd84a7..978d2f926 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java @@ -55,7 +55,8 @@ default String asString() { } /** - * Get a new {@link IStringItem} based on the the textual value of the item's "wrapped" value. + * Get a new {@link IStringItem} based on the the textual value of the item's + * "wrapped" value. * * @return a new string item */ diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java index d5ff92089..cd266ed77 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java @@ -69,4 +69,7 @@ static IDecimalItem valueOf(@NonNull String value) { throws InvalidValueForCastFunctionException { return MetaschemaDataTypeProvider.DECIMAL.cast(item); } + + @Override + BigDecimal getValue(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java index d19080d74..7ab5bb7da 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java @@ -33,7 +33,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public interface IIntegerItem extends IDecimalItem { +public interface IIntegerItem extends INumericItem { @SuppressWarnings("null") @NonNull @@ -59,7 +59,8 @@ static IIntegerItem valueOf(long value) { * an integer value * @return the item * @throws NumberFormatException - * if the provided value is not a valid representation of a {@link BigInteger} + * if the provided value is not a valid representation of a + * {@link BigInteger} */ @NonNull static IIntegerItem valueOf(@NonNull String value) { @@ -98,4 +99,7 @@ default IIntegerItem ceiling() { default IIntegerItem floor() { return this; } + + @Override + BigInteger getValue(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/DocumentNodeItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/DocumentNodeItemImpl.java index 4e2459ce7..fd3fd69d1 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/DocumentNodeItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/DocumentNodeItemImpl.java @@ -60,7 +60,8 @@ public IRootAssemblyNodeItem getRootAssemblyNodeItem() { } @Override - public @NonNull URI getDocumentUri() { + @NonNull + public URI getDocumentUri() { return documentUri; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDocumentNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDocumentNodeItem.java index c056cba71..04e3ee9e9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDocumentNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDocumentNodeItem.java @@ -36,7 +36,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -public interface IDocumentNodeItem extends INodeItem { +public interface IDocumentNodeItem extends INodeItem, IFeatureNoDataItem { @Override default NodeItemType getNodeItemType() { return NodeItemType.DOCUMENT; @@ -101,9 +101,4 @@ default RESULT accept(@NonNull INodeItemVisitor - * If the provided node item is a document, this method get the first child node item's value, since - * a document doesn't have a value. + * If the provided node item is a document, this method get the first child node + * item's value, since a document doesn't have a value. * * @param * the type of the bound object to return @@ -87,15 +81,17 @@ static CLASS toValue(@NonNull INodeItem item) { /** * Retrieve the parent node item if it exists. * - * @return the parent node item, or {@code null} if this node item has no known parent + * @return the parent node item, or {@code null} if this node item has no known + * parent */ INodeItem getParentNodeItem(); /** - * Retrieve the parent content node item if it exists. A content node is a non-document node. + * Retrieve the parent content node item if it exists. A content node is a + * non-document node. * - * @return the parent content node item, or {@code null} if this node item has no known parent - * content node item + * @return the parent content node item, or {@code null} if this node item has + * no known parent content node item */ IModelNodeItem getParentContentNodeItem(); @@ -140,8 +136,8 @@ default Stream getPathStream() { } /** - * Get a stream of all ancestors of this node item. The stream is ordered from closest to farthest - * ancestor. + * Get a stream of all ancestors of this node item. The stream is ordered from + * closest to farthest ancestor. * * @return a stream of ancestor node items */ @@ -151,8 +147,8 @@ default Stream ancestor() { } /** - * Get a stream of this and all ancestors of this node item. The stream is ordered from self, then - * closest to farthest ancestor. + * Get a stream of this and all ancestors of this node item. The stream is + * ordered from self, then closest to farthest ancestor. * * @return a stream of this node followed by all ancestor node items */ @@ -162,8 +158,8 @@ default Stream ancestorOrSelf() { } /** - * Get a stream of the ancestors of the provided {@code item}. The stream is ordered from the - * closest to farthest ancestor. + * Get a stream of the ancestors of the provided {@code item}. The stream is + * ordered from the closest to farthest ancestor. * * @param item * the target item to get ancestors for @@ -177,8 +173,8 @@ static Stream ancestorsOf(@NonNull INodeItem item) { } /** - * Get a stream of all descendant model items of this node item. The stream is ordered from closest - * to farthest descendants in a depth-first order. + * Get a stream of all descendant model items of this node item. The stream is + * ordered from closest to farthest descendants in a depth-first order. * * @return a stream of descendant node items */ @@ -188,8 +184,9 @@ default Stream descendant() { } /** - * Get a stream of all descendant model items of the provided {@code item}. The stream is ordered - * from closest to farthest descendants in a depth-first order. + * Get a stream of all descendant model items of the provided {@code item}. The + * stream is ordered from closest to farthest descendants in a depth-first + * order. * * @param item * the target item to get descendants for @@ -207,8 +204,9 @@ static Stream decendantsOf(@NonNull INodeItem item) { } /** - * Get a stream of this node, followed by all descendant model items of this node item. The stream - * is ordered from closest to farthest descendants in a depth-first order. + * Get a stream of this node, followed by all descendant model items of this + * node item. The stream is ordered from closest to farthest descendants in a + * depth-first order. * * @return a stream of this node and descendant node items */ @@ -218,10 +216,11 @@ default Stream descendantOrSelf() { } /** - * Get the flags and value data associated this node. The resulting collection is expected to be - * ordered, with the results in document order. + * Get the flags and value data associated this node. The resulting collection + * is expected to be ordered, with the results in document order. *

- * The resulting collection may be modified, but such modification is not thread safe + * The resulting collection may be modified, but such modification is not thread + * safe * * @return a collection of flags */ @@ -233,7 +232,8 @@ default Stream descendantOrSelf() { * * @param name * the effective name of the flag - * @return the flag with the matching effective name or {@code null} if no match was found + * @return the flag with the matching effective name or {@code null} if no match + * was found */ @Nullable IFlagNodeItem getFlagByName(@NonNull String name); @@ -250,32 +250,38 @@ default Stream flags() { } /** - * Get the model items (i.e., fields, assemblies) and value data associated this node. A given model - * instance can be multi-valued, so the value of each instance will be a list. The resulting - * collection is expected to be ordered, with the results in document order. + * Get the model items (i.e., fields, assemblies) and value data associated this + * node. A given model instance can be multi-valued, so the value of each + * instance will be a list. The resulting collection is expected to be ordered, + * with the results in document order. *

- * The resulting collection may be modified, but such modification is not thread safe + * The resulting collection may be modified, but such modification is not thread + * safe * - * @return a collection of list(s), with each list containing the items for a given model instance + * @return a collection of list(s), with each list containing the items for a + * given model instance */ @NonNull Collection>> getModelItems(); /** - * Get the collection of model items associated with the instance having the provided {@code name}. + * Get the collection of model items associated with the instance having the + * provided {@code name}. *

- * The resulting collection may be modified, but such modification is not thread safe + * The resulting collection may be modified, but such modification is not thread + * safe * * @param name * the instance name to get model items for - * @return the sequence of items associated with the named model instance, or an empty list if an - * instance with that name is not present + * @return the sequence of items associated with the named model instance, or an + * empty list if an instance with that name is not present */ @NonNull List> getModelItemsByName(@NonNull String name); /** - * Get the model items (i.e., fields, assemblies) and value data associated this node as a stream. + * Get the model items (i.e., fields, assemblies) and value data associated this + * node as a stream. * * @return the stream of model items or an empty stream if none exist */ diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/IResourceLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/IResourceLoader.java index a61dbcd2c..d8ebc9d3e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/IResourceLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/IResourceLoader.java @@ -26,15 +26,6 @@ package gov.nist.secauto.metaschema.core.model; -import gov.nist.secauto.metaschema.core.model.util.InputSourceUtils; - -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - -import java.io.IOException; -import java.net.URI; - -import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; /** @@ -47,23 +38,9 @@ public interface IResourceLoader { * @return the entity resolver */ @Nullable - default EntityResolver getEntityResolver() { - // by default, do not support an entity resolver extension mechanism - // Subclasses can override this behavior + default IUriResolver getUriResolver() { + // by default, do not support external URI resolution. Subclasses can override + // this behavior return null; } - - /** - * Create a new input source based on the provided URI. - * - * @param uri - * the resource to use as input - * @return the created input source - * @throws IOException - * if an error occurred while accessing the resource identified by the URI - */ - @NonNull - default InputSource toInputSource(@NonNull URI uri) throws IOException { - return InputSourceUtils.toInputSource(uri, getEntityResolver()); - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/IUriResolver.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/IUriResolver.java new file mode 100644 index 000000000..0cd3507c1 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/IUriResolver.java @@ -0,0 +1,44 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.model; + +import java.net.URI; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public interface IUriResolver { + /** + * Resolve the provided URI, producing a resolved URI, which may point to a + * different resource than the provided URI. + * + * @param uri + * the URI to resolve + * @return the resulting URI + */ + @NonNull + URI resolve(@NonNull URI uri); +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/InputSourceUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/InputSourceUtils.java index c7e75c867..77cf4f44f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/InputSourceUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/InputSourceUtils.java @@ -36,6 +36,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +// REFACTOR: remove public final class InputSourceUtils { private InputSourceUtils() { // disable construction diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/JsonUtil.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/JsonUtil.java similarity index 93% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/JsonUtil.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/model/util/JsonUtil.java index 458d3163c..878e9f3eb 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/JsonUtil.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/util/JsonUtil.java @@ -24,7 +24,7 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.io.json; +package gov.nist.secauto.metaschema.core.model.util; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; @@ -32,13 +32,15 @@ import gov.nist.secauto.metaschema.core.util.CustomCollectors; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import gov.nist.secauto.metaschema.databind.model.IBoundNamedInstance; -import gov.nist.secauto.metaschema.databind.model.IClassBinding; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.json.JSONObject; +import org.json.JSONTokener; import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -53,6 +55,16 @@ private JsonUtil() { // disable construction } + @NonNull + public static JSONObject toJsonObject(@NonNull InputStream schemaInputStream) { + return new JSONObject(new JSONTokener(schemaInputStream)); + } + + @NonNull + public static JSONObject toJsonObject(@NonNull Reader reader) { + return new JSONObject(new JSONTokener(reader)); + } + /** * Generate an informational string describing the token at the current location * of the provided {@code parser}. @@ -266,19 +278,4 @@ public static CharSequence generateLocationMessage(@NonNull JsonLocation locatio .append(location.getColumnNr()) .append('\''); } - - @SuppressWarnings("null") - @NonNull - public static String toLocationContext( - @NonNull JsonParser parser, - @NonNull IClassBinding classBinding, - IBoundNamedInstance property) { - return new StringBuilder() - .append("property '") - .append(property.getEffectiveName()) - .append("' on class '") - .append(classBinding.getBoundClass().getName()) - .append('\'') - .append(generateLocationMessage(parser)).toString(); - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/AbstractContentValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/AbstractContentValidator.java new file mode 100644 index 000000000..04a83179a --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/AbstractContentValidator.java @@ -0,0 +1,50 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.model.validation; + +import gov.nist.secauto.metaschema.core.resource.AbstractResourceLoader; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; + +public abstract class AbstractContentValidator + extends AbstractResourceLoader + implements IContentValidator { + + @Override + public IValidationResult validate(URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); + + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return validate(is, resourceUri); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/IContentValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/IContentValidator.java index a391a9820..3b086da7b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/IContentValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/IContentValidator.java @@ -26,17 +26,18 @@ package gov.nist.secauto.metaschema.core.model.validation; -import gov.nist.secauto.metaschema.core.model.IResourceLoader; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.json.JSONObject; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -45,9 +46,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * A common interface for Metaschema related content validators. + * A common interface for validation of Metaschema module-based content. */ -public interface IContentValidator extends IResourceLoader { +public interface IContentValidator { /** * Validate the resource at provided {@code path}. * @@ -59,12 +60,13 @@ public interface IContentValidator extends IResourceLoader { */ @NonNull default IValidationResult validate(@NonNull Path path) throws IOException { - InputSource source = toInputSource(ObjectUtils.notNull(path.toUri())); - return validate(source); + try (InputStream is = ObjectUtils.notNull(Files.newInputStream(path))) { + return validate(is, ObjectUtils.notNull(path.toUri())); + } } /** - * Validate the resource at provided {@code path}. + * Validate the resource at the provided {@code url}. * * @param url * the resource to validate @@ -76,21 +78,34 @@ default IValidationResult validate(@NonNull Path path) throws IOException { */ @NonNull default IValidationResult validate(@NonNull URL url) throws IOException, URISyntaxException { - InputSource source = toInputSource(ObjectUtils.notNull(url.toURI())); - return validate(source); + return validate(ObjectUtils.notNull(url.toURI())); } + /** + * Validate the resource identified by the provided {@code uri}. + * + * @param uri + * the resource to validate + * @return the result of the validation + * @throws IOException + * if an error occurred while performing validation + */ + @NonNull + IValidationResult validate(@NonNull URI uri) throws IOException; + /** * Validate the resource associated with the provided input stream {@code is}. * - * @param source - * information about how to access the resource + * @param is + * an input stream to access the resource + * @param documentUri + * the URI of the resource to validate * @return the result of the validation * @throws IOException * if an error occurred while performing validation */ @NonNull - IValidationResult validate(@NonNull InputSource source) throws IOException; + IValidationResult validate(@NonNull InputStream is, @NonNull URI documentUri) throws IOException; /** * Validate the target using the provided XML schemas. @@ -106,7 +121,7 @@ default IValidationResult validate(@NonNull URL url) throws IOException, URISynt * if an error occurred while parsing the XML target or schema */ @NonNull - static IValidationResult validateWithXmlSchema(@NonNull Path target, @NonNull List schemaSources) + static IValidationResult validateWithXmlSchema(@NonNull URI target, @NonNull List schemaSources) throws IOException, SAXException { return new XmlSchemaContentValidator(schemaSources).validate(target); } @@ -121,10 +136,11 @@ static IValidationResult validateWithXmlSchema(@NonNull Path target, @NonNull Li * @return the validation result * @throws IOException * if an error occurred while performing validation - * @see JsonSchemaContentValidator#toJsonObject(InputStream) + * @see JsonUtil#toJsonObject(InputStream) + * @see JsonUtil#toJsonObject(java.io.Reader) */ @NonNull - static IValidationResult validateWithJsonSchema(@NonNull Path target, @NonNull JSONObject schema) + static IValidationResult validateWithJsonSchema(@NonNull URI target, @NonNull JSONObject schema) throws IOException { return new JsonSchemaContentValidator(schema).validate(target); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/JsonSchemaContentValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/JsonSchemaContentValidator.java index 38ef7b441..23b8ee742 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/JsonSchemaContentValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/JsonSchemaContentValidator.java @@ -35,13 +35,11 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; -import org.xml.sax.InputSource; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.net.URI; -import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -50,61 +48,40 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class JsonSchemaContentValidator implements IContentValidator { +public class JsonSchemaContentValidator + extends AbstractContentValidator { @NonNull private final Schema schema; - @NonNull - public static JSONObject toJsonObject(@NonNull InputStream schemaInputStream) { - return new JSONObject(new JSONTokener(schemaInputStream)); + public JsonSchemaContentValidator(@NonNull Reader reader) { + this(new JSONTokener(reader)); } - @NonNull - public static JSONObject toJsonObject(@NonNull Reader reader) { - return new JSONObject(new JSONTokener(reader)); + public JsonSchemaContentValidator(@NonNull InputStream is) { + this(new JSONTokener(is)); } - public JsonSchemaContentValidator(@NonNull InputStream schemaInputStream) { - this(toJsonObject(Objects.requireNonNull(schemaInputStream, "schemaInputStream"))); + public JsonSchemaContentValidator(@NonNull JSONObject jsonSchema) { + this(ObjectUtils.notNull(SchemaLoader.load(jsonSchema))); } - @SuppressWarnings("null") - public JsonSchemaContentValidator(@NonNull JSONObject jsonSchema) { - this(SchemaLoader.load(Objects.requireNonNull(jsonSchema, "jsonSchema"))); + protected JsonSchemaContentValidator(@NonNull JSONTokener tokenizer) { + this(new JSONObject(tokenizer)); } protected JsonSchemaContentValidator(@NonNull Schema schema) { this.schema = ObjectUtils.requireNonNull(schema, "schema"); } - @NonNull - public Schema getSchema() { - return schema; - } - @Override - public IValidationResult validate(@NonNull InputSource source) throws IOException { - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - + public IValidationResult validate(InputStream is, URI documentUri) throws IOException { JSONObject json; try { - if (source.getCharacterStream() != null) { - // attempt to use a provided character stream - json = new JSONObject(new JSONTokener(source.getCharacterStream())); - } else if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - json = new JSONObject(new JSONTokener(source.getByteStream())); - } else { - // fall back to a URL-based connection - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - json = new JSONObject(new JSONTokener(is)); - } - } + json = new JSONObject(new JSONTokener(is)); } catch (JSONException ex) { - throw new IOException(String.format("Unable to parse JSON from '%s'", uri), ex); + throw new IOException(String.format("Unable to parse JSON from '%s'", documentUri), ex); } - return validate(json, uri); + return validate(json, documentUri); } @SuppressWarnings("null") @@ -189,5 +166,4 @@ public List getFindings() { } } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java index f06b24f68..d841dd425 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java @@ -30,15 +30,12 @@ import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import java.io.IOException; import java.io.InputStream; -import java.io.Reader; import java.net.URI; -import java.net.URL; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -52,7 +49,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class XmlSchemaContentValidator implements IContentValidator { +public class XmlSchemaContentValidator + extends AbstractContentValidator { private final Schema schema; @SuppressWarnings("null") @@ -83,35 +81,10 @@ public Schema getSchema() { return schema; } - @SuppressWarnings("resource") @Override - public IValidationResult validate(@NonNull InputSource source) throws IOException { - String systemId = source.getSystemId(); - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - - IValidationResult retval; - if (source.getCharacterStream() != null) { - // attempt to use a provided character stream - try (Reader reader = source.getCharacterStream()) { - retval = validate(new StreamSource(reader, systemId), uri); - } - } else if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - try (InputStream inputStream = source.getByteStream()) { - retval = validate(new StreamSource(inputStream, systemId), uri); - } - } else { - // fall back to a URL-based connection - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - retval = validate(new StreamSource(is, systemId), uri); - } - } - return retval; - } + public IValidationResult validate(InputStream is, URI documentUri) throws IOException { + Source xmlSource = new StreamSource(is, documentUri.toASCIIString()); - @NonNull - public IValidationResult validate(Source xmlSource, @NonNull URI documentUri) throws IOException { Validator validator = schema.newValidator(); XmlValidationErrorHandler errorHandler = new XmlValidationErrorHandler(documentUri); validator.setErrorHandler(errorHandler); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/resource/AbstractResourceLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/resource/AbstractResourceLoader.java new file mode 100644 index 000000000..0f989e03b --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/resource/AbstractResourceLoader.java @@ -0,0 +1,64 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.resource; + +import gov.nist.secauto.metaschema.core.model.IResourceLoader; +import gov.nist.secauto.metaschema.core.model.IUriResolver; + +import java.net.URI; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class AbstractResourceLoader implements IResourceLoader { + /** + * An {@link IUriResolver} is not provided by default. + */ + private IUriResolver uriResolver; + + @Override + public IUriResolver getUriResolver() { + return uriResolver; + } + + public void setUriResolver(@NonNull IUriResolver uriResolver) { + this.uriResolver = uriResolver; + } + + /** + * Resolve the provided URI, producing a resolved URI, which may point to a + * different resource than the provided URI. + * + * @param uri + * the URI to resolve + * @return the resulting URI + */ + @NonNull + protected URI resolve(@NonNull URI uri) { + IUriResolver resolver = getUriResolver(); + return resolver == null ? uri : resolver.resolve(uri); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/resource/package-info.java b/core/src/main/java/gov/nist/secauto/metaschema/core/resource/package-info.java new file mode 100644 index 000000000..5ca1034a2 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/resource/package-info.java @@ -0,0 +1,27 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.resource; diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index 059168b93..0d2e122f3 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -59,7 +59,7 @@ requires transitive org.codehaus.stax2; // dependencies without a module descriptor - requires everit.json.schema; + requires transitive everit.json.schema; // needed for validation details requires transitive flexmark; requires flexmark.ext.escaped.character; requires flexmark.ext.gfm.strikethrough; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DefaultBoundLoader.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DefaultBoundLoader.java index 37f769151..c5185f6d8 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DefaultBoundLoader.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DefaultBoundLoader.java @@ -26,57 +26,30 @@ package gov.nist.secauto.metaschema.databind.io; -import com.ctc.wstx.stax.WstxInputFactory; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.format.DataFormatDetector; -import com.fasterxml.jackson.core.format.DataFormatMatcher; -import com.fasterxml.jackson.core.format.MatchStrength; -import com.fasterxml.jackson.dataformat.xml.XmlFactory; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration; import gov.nist.secauto.metaschema.core.configuration.IConfiguration; import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.resource.AbstractResourceLoader; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; -import gov.nist.secauto.metaschema.databind.io.json.JsonFactoryFactory; -import gov.nist.secauto.metaschema.databind.io.json.JsonUtil; -import gov.nist.secauto.metaschema.databind.io.yaml.YamlFactoryFactory; - -import org.codehaus.stax2.XMLEventReader2; -import org.codehaus.stax2.XMLInputFactory2; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; import java.io.BufferedInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; -import java.nio.charset.Charset; -import java.nio.file.Path; import java.util.Map; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.StartElement; - import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; /** * A default implementation of an {@link IBoundLoader}. */ -public class DefaultBoundLoader implements IBoundLoader { +public class DefaultBoundLoader + extends AbstractResourceLoader + implements IBoundLoader { public static final int LOOK_AHEAD_BYTES = 32_768; // @NonNull // private static final JsonFactory JSON_FACTORY = new JsonFactory(); @@ -85,7 +58,9 @@ public class DefaultBoundLoader implements IBoundLoader { // @NonNull // private static final YAMLFactory YAML_FACTORY = new YAMLFactory(); - private JsonFactory[] detectorFactory; + private FormatDetector formatDetector; + + private ModelDetector modelDetector; @NonNull private final IBindingContext bindingContext; @@ -93,13 +68,8 @@ public class DefaultBoundLoader implements IBoundLoader { private final IMutableConfiguration> configuration; /** - * An {@link EntityResolver} is not provided by default. - */ - @Nullable - private EntityResolver entityResolver; - - /** - * Construct a new OSCAL loader instance, using the provided {@link IBindingContext}. + * Construct a new OSCAL loader instance, using the provided + * {@link IBindingContext}. * * @param bindingContext * the Metaschema binding context to use to load Java types @@ -109,14 +79,9 @@ public DefaultBoundLoader(@NonNull IBindingContext bindingContext) { this.configuration = new DefaultConfiguration<>(); } - @Override - public IBoundLoader enableFeature(DeserializationFeature feature) { - return set(feature, true); - } - - @Override - public IBoundLoader disableFeature(DeserializationFeature feature) { - return set(feature, false); + @NonNull + protected IMutableConfiguration> getConfiguration() { + return configuration; } @Override @@ -139,12 +104,7 @@ public IBoundLoader applyConfiguration(@NonNull IConfiguration> getConfiguration() { - return configuration; + formatDetector = null; } @Override @@ -154,126 +114,70 @@ public IBoundLoader set(DeserializationFeature feature, Object value) { return this; } - @Override - public V get(DeserializationFeature feature) { - return getConfiguration().get(feature); - } - @Override public IBindingContext getBindingContext() { return bindingContext; } @Override - public EntityResolver getEntityResolver() { - return entityResolver; - } - - @Override - public void setEntityResolver(@NonNull EntityResolver resolver) { - this.entityResolver = resolver; - } + public FormatDetector.Result detectFormat(@NonNull URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); - @Override - public Format detectFormat(InputSource source) throws IOException { - Format retval; - if (source.getCharacterStream() != null) { - throw new UnsupportedOperationException("Character streams are not supported"); - } else if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - retval = detectFormatInternal(ObjectUtils.notNull(source.getByteStream())); - } else { - // fall back to a URL-based connection - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - retval = detectFormatInternal(ObjectUtils.notNull(is)); - } + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return detectFormat(is); } - return retval; - } - - @NonNull - protected Format detectFormatInternal(@NonNull InputStream is) throws IOException { - return detectFormatInternal(is, LOOK_AHEAD_BYTES - 1); - } - - @NonNull - protected Format detectFormatInternal(@NonNull InputStream is, int lookAheadBytes) throws IOException { - DataFormatMatcher matcher = matchFormat(is, lookAheadBytes); - return formatFromMatcher(matcher); } - private JsonFactory[] getDetectorFactory() { - if (detectorFactory == null) { - detectorFactory = new JsonFactory[3]; - detectorFactory[0] = YamlFactoryFactory.newParserFactoryInstance(getConfiguration()); - detectorFactory[1] = JsonFactoryFactory.instance(); - detectorFactory[2] = new XmlFactory(); - } - return detectorFactory; + @Override + public FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException { + return getFormatDetector().detect(is); } @NonNull - protected DataFormatMatcher matchFormat(@NonNull InputStream is, int lookAheadBytes) throws IOException { - DataFormatDetector det = new DataFormatDetector(getDetectorFactory()); - det = det.withMinimalMatch(MatchStrength.INCONCLUSIVE).withOptimalMatch(MatchStrength.SOLID_MATCH) - .withMaxInputLookahead(lookAheadBytes); - - DataFormatMatcher matcher = det.findFormat(is); - switch (matcher.getMatchStrength()) { - case FULL_MATCH: - case SOLID_MATCH: - case WEAK_MATCH: - case INCONCLUSIVE: - return matcher; - case NO_MATCH: - default: - throw new UnsupportedOperationException("Unable to identify format"); + private FormatDetector getFormatDetector() { + if (formatDetector == null) { + formatDetector = new FormatDetector(getConfiguration()); } + assert formatDetector != null; + return formatDetector; } @NonNull - protected Format formatFromMatcher(@NonNull DataFormatMatcher matcher) { - Format retval; - String formatName = matcher.getMatchedFormatName(); - if (YAMLFactory.FORMAT_NAME_YAML.equals(formatName)) { - retval = Format.YAML; - } else if (JsonFactory.FORMAT_NAME_JSON.equals(formatName)) { - retval = Format.JSON; - } else if (XmlFactory.FORMAT_NAME_XML.equals(formatName)) { - retval = Format.XML; - } else { - throw new UnsupportedOperationException(String.format("The detected format '%s' is not supported", formatName)); + private ModelDetector getModelDetector() { + if (modelDetector == null) { + modelDetector = new ModelDetector( + getBindingContext(), + getConfiguration()); } - return retval; + assert modelDetector != null; + return modelDetector; } - @NonNull - private static BufferedInputStream toBufferedInputStream(@NonNull InputStream is) { - BufferedInputStream bis = new BufferedInputStream(is, LOOK_AHEAD_BYTES); // NOPMD - stream not owned - bis.mark(LOOK_AHEAD_BYTES); - return bis; - } - - @Override - public CLASS load(@NonNull URL url) throws IOException, URISyntaxException { - // TODO: avoid node item - return INodeItem.toValue(loadAsNodeItem(url)); - } + // + // @NonNull + // private static BufferedInputStream toBufferedInputStream(@NonNull InputStream + // is) { + // return toBufferedInputStream(is, LOOK_AHEAD_BYTES); + // } + // + // @NonNull + // private static BufferedInputStream toBufferedInputStream(@NonNull InputStream + // is, int lookaheadSize) { + // BufferedInputStream bis = new BufferedInputStream(is, lookaheadSize); // + // NOPMD - stream not owned + // bis.mark(lookaheadSize); + // return bis; + // } @Override - @NonNull - public CLASS load(@NonNull Path path) throws IOException { - // TODO: avoid node item - return INodeItem.toValue(loadAsNodeItem(path)); - } + public CLASS load(@NonNull URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); - @Override - @NonNull - public CLASS load(@NonNull File file) throws IOException { - // TODO: avoid node item - return INodeItem.toValue(loadAsNodeItem(file)); + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return load(is, uri); + } } @Override @@ -284,104 +188,80 @@ public CLASS load(@NonNull InputStream is, @NonNull URI documentUri) thr } @Override - @NonNull - public CLASS load(@NonNull InputSource source) throws IOException { - return INodeItem.toValue(loadAsNodeItem(source)); + public CLASS load(Class clazz, URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); + + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return load(clazz, is, resourceUri); + } } @Override - public CLASS load(Class clazz, InputSource source) throws IOException { - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - - CLASS retval; - if (source.getCharacterStream() != null) { - throw new UnsupportedOperationException("Character streams are not supported"); - } else if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - try (BufferedInputStream bis = new BufferedInputStream(source.getByteStream(), LOOK_AHEAD_BYTES)) { - retval = loadInternal(clazz, bis, uri); - } - } else { - // fall back to a URL-based connection - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - try (BufferedInputStream bis = new BufferedInputStream(is, LOOK_AHEAD_BYTES)) { - retval = loadInternal(clazz, bis, uri); - } - } + public CLASS load(Class clazz, InputStream is, URI documentUri) throws IOException { + // we cannot close this stream, since it will cause the underlying stream to be + // closed + FormatDetector.Result match = getFormatDetector().detect(is); + Format format = match.getFormat(); + + try (InputStream remainingStream = match.getDataStream()) { + // is autoclosing ok? + return load(clazz, format, remainingStream, documentUri); } - return retval; } @NonNull - protected CLASS loadInternal(@NonNull Class clazz, @NonNull BufferedInputStream bis, + private CLASS load( + @NonNull Class clazz, + @NonNull Format format, + @NonNull InputStream is, @NonNull URI documentUri) throws IOException { - // we cannot close this stream, since it will cause the underlying stream to be - // closed - bis.mark(LOOK_AHEAD_BYTES); - - Format format = detectFormatInternal(bis); - bis.reset(); IDeserializer deserializer = getDeserializer(clazz, format, getConfiguration()); - return deserializer.deserialize(bis, documentUri); + return deserializer.deserialize(is, documentUri); } @Override - public IDocumentNodeItem loadAsNodeItem(InputSource source) throws IOException { - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - - IDocumentNodeItem retval; - if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - try (BufferedInputStream bis = toBufferedInputStream(ObjectUtils.notNull(source.getByteStream()))) { - retval = loadAsNodeItemInternal(bis, uri); - } - } else { - // fall back to a URL-based connection - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - try (BufferedInputStream bis = toBufferedInputStream(ObjectUtils.notNull(is))) { - retval = loadAsNodeItemInternal(bis, uri); - } - } + public IDocumentNodeItem loadAsNodeItem(URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); + + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return loadAsNodeItem(is, resourceUri); } - return retval; } @Override - public IDocumentNodeItem loadAsNodeItem(@NonNull Format format, @NonNull InputSource source) - throws IOException { - URI uri = ObjectUtils.notNull(URI.create(source.getSystemId())); - - IDocumentNodeItem retval; - if (source.getByteStream() != null) { - // attempt to use a provided byte stream stream - try (@SuppressWarnings("resource") BufferedInputStream bis = toBufferedInputStream( - ObjectUtils.requireNonNull(source.getByteStream()))) { - Class clazz = detectModel(bis, format); // NOPMD - must be called before reset - retval = deserializeToNodeItem(clazz, format, bis, uri); - } - } else { - // fall back to a URL-based connection - URL url = uri.toURL(); - try (InputStream is = url.openStream()) { - try (BufferedInputStream bis = toBufferedInputStream(ObjectUtils.notNull(is))) { - Class clazz = detectModel(bis, format); // NOPMD - must be called before reset - retval = deserializeToNodeItem(clazz, format, bis, uri); - } - } + public IDocumentNodeItem loadAsNodeItem(InputStream is, URI documentUri) throws IOException { + FormatDetector.Result formatMatch = getFormatDetector().detect(is); + Format format = formatMatch.getFormat(); + + try (InputStream formatStream = formatMatch.getDataStream()) { + return loadAsNodeItem(format, formatStream, documentUri); } - return retval; } - @NonNull - protected IDocumentNodeItem loadAsNodeItemInternal(@NonNull BufferedInputStream bis, @NonNull URI documentUri) - throws IOException { - DataFormatMatcher matcher = matchFormat(bis, LOOK_AHEAD_BYTES - 1); - Format format = formatFromMatcher(matcher); - Class clazz = detectModel(matcher, format); // NOPMD - must be called before reset - return deserializeToNodeItem(clazz, format, bis, documentUri); + @Override + public IDocumentNodeItem loadAsNodeItem(Format format, URI uri) throws IOException { + URI resourceUri = resolve(uri); + URL resource = resourceUri.toURL(); + + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return loadAsNodeItem(format, is, resourceUri); + } + } + + @Override + public IDocumentNodeItem loadAsNodeItem(Format format, InputStream is, URI documentUri) throws IOException { + ModelDetector.Result modelMatch = getModelDetector().detect(is, format); + + IDeserializer deserializer = getDeserializer( + modelMatch.getBoundClass(), + format, + getConfiguration()); + try (InputStream modelStream = modelMatch.getDataStream()) { + return (IDocumentNodeItem) deserializer.deserializeToNodeItem(modelStream, documentUri); + } } @NonNull @@ -400,127 +280,6 @@ protected IDocumentNodeItem deserializeToNodeItem( return (IDocumentNodeItem) deserializer.deserializeToNodeItem(bis, documentUri); } - @NonNull - protected Class detectModel(@NonNull BufferedInputStream bis, @NonNull Format format) - throws IOException { - Class clazz; - switch (format) { - case JSON: - clazz = detectModelJsonClass(ObjectUtils.notNull(JsonFactoryFactory.instance().createParser(bis))); - if (clazz == null) { - throw new IllegalStateException( - String.format("Detected format '%s', but unable to detect the bound data type", format.name())); - } - break; - case YAML: - YAMLFactory factory = YamlFactoryFactory.newParserFactoryInstance(getConfiguration()); - clazz = detectModelJsonClass(ObjectUtils.notNull(factory.createParser(bis))); - if (clazz == null) { - throw new IllegalStateException( - String.format("Detected format '%s', but unable to detect the bound data type", format.name())); - } - break; - case XML: - clazz = detectModelXmlClass(ObjectUtils.notNull(bis)); - break; - default: - throw new UnsupportedOperationException( - String.format("The format '%s' is not supported", format)); - } - - try { - bis.reset(); - } catch (IOException ex) { - throw new IOException("Unable to reset input stream before parsing", ex); - } - return clazz; - } - - @NonNull - protected Class detectModel(@NonNull DataFormatMatcher matcher, @NonNull Format format) - throws IOException { - Class clazz; - switch (format) { - case JSON: - case YAML: - clazz = detectModelJsonClass(ObjectUtils.notNull(matcher.createParserWithMatch())); - if (clazz == null) { - throw new IllegalStateException( - String.format("Detected format '%s', but unable to detect the bound data type", format.name())); - } - break; - case XML: - clazz = detectModelXmlClass(ObjectUtils.notNull(matcher.getDataStream())); - break; - default: - throw new UnsupportedOperationException( - String.format("The detected format '%s' is not supported", matcher.getMatchedFormatName())); - } - return clazz; - } - - @NonNull - protected Class detectModelXmlClass(@NonNull InputStream is) throws IOException { - - QName startElementQName; - try { - XMLInputFactory2 xmlInputFactory = (XMLInputFactory2) XMLInputFactory.newInstance(); - assert xmlInputFactory instanceof WstxInputFactory; - xmlInputFactory.configureForXmlConformance(); - xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false); - - Reader reader = new InputStreamReader(is, Charset.forName("UTF8")); - XMLEventReader2 eventReader = (XMLEventReader2) xmlInputFactory.createXMLEventReader(reader); - if (eventReader.peek().isStartDocument()) { - while (eventReader.hasNext() && !eventReader.peek().isStartElement()) { - eventReader.nextEvent(); - } - } - - if (!eventReader.peek().isStartElement()) { - throw new UnsupportedOperationException("Unable to detect a start element"); - } - - StartElement start = eventReader.nextEvent().asStartElement(); - startElementQName = ObjectUtils.notNull(start.getName()); - } catch (XMLStreamException ex) { - throw new IOException(ex); - } - - Class clazz = getBoundClassForXmlQName(startElementQName); - - if (clazz == null) { - throw new UnsupportedOperationException("Unrecognized element name: " + startElementQName.toString()); - } - return clazz; - } - - protected Class getBoundClassForXmlQName(@NonNull QName rootQName) { - return getBindingContext().getBoundClassForXmlQName(rootQName); - } - - @Nullable - protected Class detectModelJsonClass(@NonNull JsonParser parser) throws IOException { - Class retval = null; - JsonUtil.advanceAndAssert(parser, JsonToken.START_OBJECT); - outer: while (JsonToken.FIELD_NAME.equals(parser.nextToken())) { - String name = ObjectUtils.notNull(parser.getCurrentName()); - if ("$schema".equals(name)) { - // do nothing - parser.nextToken(); - // JsonUtil.skipNextValue(parser); - } else { - retval = getBoundClassForJsonName(name); - break outer; - } - } - return retval; - } - - protected Class getBoundClassForJsonName(@NonNull String rootName) { - return getBindingContext().getBoundClassForJsonName(rootName); - } - @NonNull protected IDeserializer getDeserializer( @NonNull Class clazz, @@ -530,4 +289,5 @@ protected IDeserializer getDeserializer( retval.applyConfiguration(config); return retval; } + } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DeserializationFeature.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DeserializationFeature.java index 5814c84d4..eb9e6cf59 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DeserializationFeature.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/DeserializationFeature.java @@ -34,6 +34,7 @@ public final class DeserializationFeature extends AbstractConfigurationFeature { public static final int YAML_CODEPOINT_LIMIT_DEFAULT = Integer.MAX_VALUE - 1; // 2 GB + public static final int FORMAT_DETECTION_LOOKAHEAD = 32_768; // 2 GB /** * If enabled, perform constraint validation on the deserialized bound objects. @@ -44,19 +45,28 @@ public final class DeserializationFeature /** * If enabled, process the next JSON node as a field, whose name must match the - * {@link IAssemblyDefinition#getRootJsonName()}. If not enabled, the next JSON node is expected to - * be an object containing the data of the {@link IAssemblyDefinition}. + * {@link IAssemblyDefinition#getRootJsonName()}. If not enabled, the next JSON + * node is expected to be an object containing the data of the + * {@link IAssemblyDefinition}. */ public static final DeserializationFeature DESERIALIZE_JSON_ROOT_PROPERTY = new DeserializationFeature<>(Boolean.class, true); /** - * If enabled, perform constraint validation on the deserialized bound objects. + * Determines the max YAML codepoints that can be read. */ @NonNull public static final DeserializationFeature YAML_CODEPOINT_LIMIT = new DeserializationFeature<>(Integer.class, YAML_CODEPOINT_LIMIT_DEFAULT); + /** + * Determines how many bytes can be looked at to identify the format of a + * document. + */ + @NonNull + public static final DeserializationFeature FORMAT_DETECTION_LOOKAHEAD_LIMIT + = new DeserializationFeature<>(Integer.class, FORMAT_DETECTION_LOOKAHEAD); + private DeserializationFeature( @NonNull Class valueClass, @NonNull V defaultValue) { diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/FormatDetector.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/FormatDetector.java new file mode 100644 index 000000000..38a884439 --- /dev/null +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/FormatDetector.java @@ -0,0 +1,146 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.databind.io; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.format.DataFormatDetector; +import com.fasterxml.jackson.core.format.DataFormatMatcher; +import com.fasterxml.jackson.core.format.MatchStrength; +import com.fasterxml.jackson.dataformat.xml.XmlFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration; +import gov.nist.secauto.metaschema.core.configuration.IConfiguration; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import gov.nist.secauto.metaschema.databind.io.json.JsonFactoryFactory; +import gov.nist.secauto.metaschema.databind.io.yaml.YamlFactoryFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class FormatDetector { + + private final int lookaheadBytes; + private final DataFormatDetector detector; + + public FormatDetector() { + this(new DefaultConfiguration<>()); + } + + public FormatDetector( + @NonNull IConfiguration> config) { + this(config, newDetectorFactory(config)); + } + + protected FormatDetector( + @NonNull IConfiguration> config, + @NonNull JsonFactory... detectors) { + this.lookaheadBytes = config.get(DeserializationFeature.FORMAT_DETECTION_LOOKAHEAD_LIMIT); + this.detector = new DataFormatDetector(detectors) + .withMinimalMatch(MatchStrength.INCONCLUSIVE) + .withOptimalMatch(MatchStrength.SOLID_MATCH) + .withMaxInputLookahead(this.lookaheadBytes - 1); + + } + + @NonNull + private static JsonFactory[] newDetectorFactory(@NonNull IConfiguration> config) { + JsonFactory[] detectorFactory = new JsonFactory[3]; + detectorFactory[0] = YamlFactoryFactory.newParserFactoryInstance(config); + detectorFactory[1] = JsonFactoryFactory.instance(); + detectorFactory[2] = new XmlFactory(); + return detectorFactory; + } + + @NonNull + public Result detect(@NonNull URL resource) throws IOException { + try (InputStream is = ObjectUtils.notNull(resource.openStream())) { + return detect(is); + } + } + + @NonNull + public Result detect(@NonNull InputStream is) throws IOException { + DataFormatMatcher matcher = detector.findFormat(is); + switch (matcher.getMatchStrength()) { + case FULL_MATCH: + case SOLID_MATCH: + case WEAK_MATCH: + case INCONCLUSIVE: + return new Result(matcher); + case NO_MATCH: + default: + throw new IOException("Unable to identify format"); + } + } + + public static class Result { + @NonNull + private final DataFormatMatcher matcher; + + private Result(@NonNull DataFormatMatcher matcher) { + this.matcher = matcher; + } + + @NonNull + public Format getFormat() { + Format retval; + String formatName = matcher.getMatchedFormatName(); + if (YAMLFactory.FORMAT_NAME_YAML.equals(formatName)) { + retval = Format.YAML; + } else if (JsonFactory.FORMAT_NAME_JSON.equals(formatName)) { + retval = Format.JSON; + } else if (XmlFactory.FORMAT_NAME_XML.equals(formatName)) { + retval = Format.XML; + } else { + throw new UnsupportedOperationException(String.format("The detected format '%s' is not supported", formatName)); + } + return retval; + } + + @SuppressWarnings("resource") + @NonNull + public InputStream getDataStream() { + return ObjectUtils.notNull(matcher.getDataStream()); + } + + @SuppressWarnings("resource") + @NonNull + public JsonParser getParser() throws IOException { + return ObjectUtils.notNull(matcher.createParserWithMatch()); + } + + @NonNull + public MatchStrength getMatchStrength() { + return ObjectUtils.notNull(matcher.getMatchStrength()); + } + } +} diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/IBoundLoader.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/IBoundLoader.java index 719583fb8..afded26d8 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/IBoundLoader.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/IBoundLoader.java @@ -34,8 +34,6 @@ import gov.nist.secauto.metaschema.databind.DefaultBindingContext; import gov.nist.secauto.metaschema.databind.IBindingContext; -import org.xml.sax.InputSource; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -54,10 +52,14 @@ public interface IBoundLoader extends IDocumentLoader, IMutableConfiguration> { @Override - IBoundLoader enableFeature(DeserializationFeature feature); + default IBoundLoader enableFeature(DeserializationFeature feature) { + return set(feature, true); + } @Override - IBoundLoader disableFeature(DeserializationFeature feature); + default IBoundLoader disableFeature(DeserializationFeature feature) { + return set(feature, false); + } @Override IBoundLoader applyConfiguration(IConfiguration> other); @@ -68,17 +70,15 @@ public interface IBoundLoader extends IDocumentLoader, IMutableConfiguration @@ -121,34 +137,33 @@ default Format detectFormat(@NonNull File file) throws IOException { * * @param is * an input stream for the resource - * @return the format of the provided resource + * @return the format information for the provided resource * @throws IOException * if an error occurred while reading the resource */ @NonNull - default Format detectFormat(@NonNull InputStream is) throws IOException { - return detectFormat(new InputSource(is)); - } + FormatDetector.Result detectFormat(@NonNull InputStream is) throws IOException; - /** - * Determine the format of the provided resource. - *

- * This method will consume data from any {@link InputStream} provided by the - * {@link InputSource}. If the caller of this method intends to read data from - * the stream after determining the format, the caller should pass in a stream - * that can be reset. - *

- * This method will not close any {@link InputStream} provided by the - * {@link InputSource}, since it does not own the stream. - * - * @param source - * information about how to access the resource - * @return the format of the provided resource - * @throws IOException - * if an error occurred while reading the resource - */ - @NonNull - Format detectFormat(@NonNull InputSource source) throws IOException; + // + // /** + // * Determine the format of the provided resource. + // *

+ // * This method will consume data from any {@link InputStream} provided by the + // * {@link InputSource}. If the caller of this method intends to read data from + // * the stream after determining the format, the caller should pass in a stream + // * that can be reset. + // *

+ // * This method will not close any {@link InputStream} provided by the + // * {@link InputSource}, since it does not own the stream. + // * + // * @param source + // * information about how to access the resource + // * @return the format of the provided resource + // * @throws IOException + // * if an error occurred while reading the resource + // */ + // @NonNull + // Format detectFormat(@NonNull InputSource source) throws IOException; /** * Load data from the provided resource into a bound object. @@ -157,17 +172,17 @@ default Format detectFormat(@NonNull InputStream is) throws IOException { * * @param * the type of the bound object to return - * @param url + * @param file * the resource * @return a bound object containing the loaded data * @throws IOException * if an error occurred while reading the resource - * @throws URISyntaxException - * if the provided {@code url} is malformed - * @see #detectFormat(URL) + * @see #detectFormat(File) */ @NonNull - CLASS load(@NonNull URL url) throws IOException, URISyntaxException; + default CLASS load(@NonNull File file) throws IOException { + return load(ObjectUtils.notNull(file.toPath())); + } /** * Load data from the provided resource into a bound object. @@ -184,7 +199,9 @@ default Format detectFormat(@NonNull InputStream is) throws IOException { * @see #detectFormat(File) */ @NonNull - CLASS load(@NonNull Path path) throws IOException; + default CLASS load(@NonNull Path path) throws IOException { + return load(ObjectUtils.notNull(path.toUri())); + } /** * Load data from the provided resource into a bound object. @@ -193,57 +210,59 @@ default Format detectFormat(@NonNull InputStream is) throws IOException { * * @param * the type of the bound object to return - * @param file + * @param url * the resource * @return a bound object containing the loaded data * @throws IOException * if an error occurred while reading the resource - * @see #detectFormat(File) + * @throws URISyntaxException + * if the provided {@code url} is malformed + * @see #detectFormat(URL) */ @NonNull - CLASS load(@NonNull File file) throws IOException; + default CLASS load(@NonNull URL url) throws IOException, URISyntaxException { + return load(ObjectUtils.notNull(url.toURI())); + } /** - * Load data from the provided resource into a bound object. - *

- * This method should auto-detect the format of the provided resource. + * Load data from the resource identified by the provided {@code uri} into a + * bound object. *

- * This method will not close the provided {@link InputStream}, since it does - * not own the stream. + * This method will auto-detect the format of the provided resource. * * @param * the type of the bound object to return - * @param is + * @param uri * the resource - * @param documentUri - * the URI of the resource * @return a bound object containing the loaded data * @throws IOException * if an error occurred while reading the resource - * @see #detectFormat(InputStream) + * @see #detectFormat(URL) */ @NonNull - CLASS load(@NonNull InputStream is, @NonNull URI documentUri) throws IOException; + CLASS load(@NonNull URI uri) throws IOException; /** * Load data from the provided resource into a bound object. *

- * This method will auto-detect the format of the provided resource. + * This method should auto-detect the format of the provided resource. *

- * This method will not close any {@link InputStream} provided by the - * {@link InputSource}, since it does not own the stream. + * This method will not close the provided {@link InputStream}, since it does + * not own the stream. * * @param * the type of the bound object to return - * @param source - * information about how to access the resource + * @param is + * the resource stream + * @param documentUri + * the URI of the resource * @return a bound object containing the loaded data * @throws IOException * if an error occurred while reading the resource - * @see #detectFormat(InputSource) + * @see #detectFormat(InputStream) */ @NonNull - CLASS load(@NonNull InputSource source) throws IOException; + CLASS load(@NonNull InputStream is, @NonNull URI documentUri) throws IOException; /** * Load data from the specified resource into a bound object with the type of @@ -253,7 +272,7 @@ default Format detectFormat(@NonNull InputStream is) throws IOException { * the Java type to load data into * @param clazz * the class for the java type - * @param path + * @param file * the resource to load * @return the loaded instance data * @throws IOException @@ -262,12 +281,8 @@ default Format detectFormat(@NonNull InputStream is) throws IOException { @NonNull default CLASS load( @NonNull Class clazz, - @NonNull Path path) throws IOException { - try { - return load(clazz, ObjectUtils.notNull(path.toUri())); - } catch (URISyntaxException ex) { - throw new IOException(ex); - } + @NonNull File file) throws IOException { + return load(clazz, ObjectUtils.notNull(file.toPath())); } /** @@ -278,7 +293,7 @@ default CLASS load( * the Java type to load data into * @param clazz * the class for the java type - * @param file + * @param path * the resource to load * @return the loaded instance data * @throws IOException @@ -287,8 +302,8 @@ default CLASS load( @NonNull default CLASS load( @NonNull Class clazz, - @NonNull File file) throws IOException { - return load(clazz, ObjectUtils.notNull(file.toPath())); + @NonNull Path path) throws IOException { + return load(clazz, ObjectUtils.notNull(path.toUri())); } /** @@ -327,15 +342,11 @@ default CLASS load( * @return the loaded instance data * @throws IOException * if an error occurred while loading the data in the specified file - * @throws URISyntaxException - * if the provided {@code url} is malformed */ @NonNull - default CLASS load( + CLASS load( @NonNull Class clazz, - @NonNull URI uri) throws IOException, URISyntaxException { - return load(clazz, toInputSource(ObjectUtils.requireNonNull(uri))); - } + @NonNull URI uri) throws IOException; /** * Load data from the specified resource into a bound object with the type of @@ -354,6 +365,10 @@ default CLASS load( * the Java type to load data into * @param clazz * the class for the java type + * @param is + * the resource stream + * @param documentUri + * the URI of the resource * @param source * information about how to access the resource * @return the loaded data @@ -364,7 +379,8 @@ default CLASS load( @NonNull CLASS load( @NonNull Class clazz, - @NonNull InputSource source) throws IOException; + @NonNull InputStream is, + @NonNull URI documentUri) throws IOException; /** * Load data expressed using the provided {@code format} and return that data as @@ -375,8 +391,54 @@ CLASS load( * * @param format * the expected format of the data to parse - * @param source - * information about how to access the resource + * @param path + * the resource + * @return the Metapath node item for the parsed data + * @throws IOException + * if an error occurred while loading the data from the specified + * resource + */ + @NonNull + default IDocumentNodeItem loadAsNodeItem( + @NonNull Format format, + @NonNull Path path) throws IOException { + return loadAsNodeItem(format, ObjectUtils.notNull(path.toUri())); + } + + /** + * Load data expressed using the provided {@code format} and return that data as + * a Metapath node item. + *

+ * The specific Metaschema model is auto-detected by analyzing the source. The + * class reported is implementation specific. + * + * @param format + * the expected format of the data to parse + * @param uri + * the resource + * @return the Metapath node item for the parsed data + * @throws IOException + * if an error occurred while loading the data from the specified + * resource + */ + @NonNull + IDocumentNodeItem loadAsNodeItem( + @NonNull Format format, + @NonNull URI uri) throws IOException; + + /** + * Load data expressed using the provided {@code format} and return that data as + * a Metapath node item. + *

+ * The specific Metaschema model is auto-detected by analyzing the source. The + * class reported is implementation specific. + * + * @param format + * the expected format of the data to parse + * @param is + * the resource stream + * @param documentUri + * the URI of the resource * @return the Metapath node item for the parsed data * @throws IOException * if an error occurred while loading the data from the specified @@ -385,13 +447,15 @@ CLASS load( @NonNull IDocumentNodeItem loadAsNodeItem( @NonNull Format format, - @NonNull InputSource source) throws IOException; + @NonNull InputStream is, + @NonNull URI documentUri) throws IOException; /** * Get the configured Metaschema binding context to use to load Java types. * * @return the binding context */ + @NonNull IBindingContext getBindingContext(); /** diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/ModelDetector.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/ModelDetector.java new file mode 100644 index 000000000..a5d455c97 --- /dev/null +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/ModelDetector.java @@ -0,0 +1,195 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.databind.io; + +import com.ctc.wstx.stax.WstxInputFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.io.MergedStream; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +import gov.nist.secauto.metaschema.core.configuration.IConfiguration; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import gov.nist.secauto.metaschema.databind.IBindingContext; +import gov.nist.secauto.metaschema.databind.io.json.JsonFactoryFactory; +import gov.nist.secauto.metaschema.databind.io.yaml.YamlFactoryFactory; + +import org.codehaus.stax2.XMLEventReader2; +import org.codehaus.stax2.XMLInputFactory2; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public class ModelDetector { + @NonNull + private final IBindingContext bindingContext; + @NonNull + private final IConfiguration> configuration; + + public ModelDetector( + @NonNull IBindingContext bindingContext, + @NonNull IConfiguration> config) { + this.bindingContext = bindingContext; + this.configuration = config; + } + + private int getLookaheadLimit() { + return configuration.get(DeserializationFeature.FORMAT_DETECTION_LOOKAHEAD_LIMIT); + } + + @NonNull + private IBindingContext getBindingContext() { + return bindingContext; + } + + @NonNull + private IConfiguration> getConfiguration() { + return configuration; + } + + @NonNull + public Result detect(@NonNull InputStream is, @NonNull Format format) + throws IOException { + byte[] buf = ObjectUtils.notNull(is.readNBytes(getLookaheadLimit())); + + Class clazz; + try (InputStream bis = new ByteArrayInputStream(buf)) { + switch (format) { + case JSON: + clazz = detectModelJsonClass(ObjectUtils.notNull( + JsonFactoryFactory.instance().createParser(bis))); + break; + case YAML: + YAMLFactory factory = YamlFactoryFactory.newParserFactoryInstance(getConfiguration()); + clazz = detectModelJsonClass(ObjectUtils.notNull(factory.createParser(bis))); + break; + case XML: + clazz = detectModelXmlClass(ObjectUtils.notNull(bis)); + break; + default: + throw new UnsupportedOperationException( + String.format("The format '%s' dataStream not supported", format)); + } + } + + if (clazz == null) { + throw new IllegalStateException( + String.format("Detected format '%s', but unable to detect the bound data type", format.name())); + } + + return new Result(clazz, is, buf); + } + + @NonNull + protected Class detectModelXmlClass(@NonNull InputStream is) throws IOException { + QName startElementQName; + try { + XMLInputFactory2 xmlInputFactory = (XMLInputFactory2) XMLInputFactory.newInstance(); + assert xmlInputFactory instanceof WstxInputFactory; + xmlInputFactory.configureForXmlConformance(); + xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, false); + + Reader reader = new InputStreamReader(is, Charset.forName("UTF8")); + XMLEventReader2 eventReader = (XMLEventReader2) xmlInputFactory.createXMLEventReader(reader); + while (eventReader.hasNext() && !eventReader.peek().isStartElement()) { + eventReader.nextEvent(); + } + + if (!eventReader.peek().isStartElement()) { + throw new IOException("Unable to detect a start element"); + } + + StartElement start = eventReader.nextEvent().asStartElement(); + startElementQName = ObjectUtils.notNull(start.getName()); + } catch (XMLStreamException ex) { + throw new IOException(ex); + } + + Class clazz = getBindingContext().getBoundClassForXmlQName(startElementQName); + + if (clazz == null) { + throw new IOException("Unrecognized element name: " + startElementQName.toString()); + } + return clazz; + } + + @Nullable + protected Class detectModelJsonClass(@NonNull JsonParser parser) throws IOException { + Class retval = null; + JsonUtil.advanceAndAssert(parser, JsonToken.START_OBJECT); + outer: while (JsonToken.FIELD_NAME.equals(parser.nextToken())) { + String name = ObjectUtils.notNull(parser.getCurrentName()); + if ("$schema".equals(name)) { + // do nothing + parser.nextToken(); + // JsonUtil.skipNextValue(parser); + } else { + retval = getBindingContext().getBoundClassForJsonName(name); + break outer; + } + } + return retval; + } + + public static class Result { + @NonNull + private final Class boundClass; + @NonNull + private final InputStream dataStream; + + private Result( + @NonNull Class clazz, + @NonNull InputStream is, + @NonNull byte[] buf) { + this.boundClass = clazz; + this.dataStream = new MergedStream(null, is, buf, 0, buf.length); + } + + @NonNull + public Class getBoundClass() { + return boundClass; + } + + @NonNull + public InputStream getDataStream() { + return dataStream; + } + } +} diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonDeserializer.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonDeserializer.java index b5cacd346..eac82a381 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonDeserializer.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonDeserializer.java @@ -33,6 +33,7 @@ import gov.nist.secauto.metaschema.core.configuration.IConfiguration; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.IBindingContext; import gov.nist.secauto.metaschema.databind.io.AbstractDeserializer; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonProblemHandler.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonProblemHandler.java index a0c1b011a..82813acf0 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonProblemHandler.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/DefaultJsonProblemHandler.java @@ -26,6 +26,7 @@ package gov.nist.secauto.metaschema.databind.io.json; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.databind.io.AbstractProblemHandler; import gov.nist.secauto.metaschema.databind.model.IBoundNamedInstance; import gov.nist.secauto.metaschema.databind.model.IClassBinding; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonParser.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonParser.java index ace1b6bfc..710625769 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonParser.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/json/MetaschemaJsonParser.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.io.BindingException; import gov.nist.secauto.metaschema.databind.model.IAssemblyClassBinding; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/yaml/YamlFactoryFactory.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/yaml/YamlFactoryFactory.java index 87a2a486d..0db293724 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/yaml/YamlFactoryFactory.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/io/yaml/YamlFactoryFactory.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactoryBuilder; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; +import gov.nist.secauto.metaschema.core.configuration.IConfiguration; import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.io.DeserializationFeature; @@ -55,7 +56,7 @@ private YamlFactoryFactory() { */ @NonNull public static YAMLFactory newParserFactoryInstance( - @NonNull IMutableConfiguration> config) { + @NonNull IConfiguration> config) { YAMLFactoryBuilder builder = YAMLFactory.builder(); LoaderOptions loaderOptions = builder.loaderOptions(); if (loaderOptions == null) { diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ClassDataTypeHandler.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ClassDataTypeHandler.java index 65578f85a..a8857b9f0 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ClassDataTypeHandler.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ClassDataTypeHandler.java @@ -30,11 +30,11 @@ import com.fasterxml.jackson.core.JsonToken; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.io.BindingException; import gov.nist.secauto.metaschema.databind.io.json.IJsonParsingContext; import gov.nist.secauto.metaschema.databind.io.json.IJsonWritingContext; -import gov.nist.secauto.metaschema.databind.io.json.JsonUtil; import gov.nist.secauto.metaschema.databind.io.xml.IXmlParsingContext; import gov.nist.secauto.metaschema.databind.io.xml.IXmlWritingContext; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ListPropertyInfo.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ListPropertyInfo.java index 8f9e56066..0c5bc62a1 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ListPropertyInfo.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/ListPropertyInfo.java @@ -31,13 +31,13 @@ import com.fasterxml.jackson.core.JsonToken; import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.io.BindingException; import gov.nist.secauto.metaschema.databind.io.json.IJsonParsingContext; import gov.nist.secauto.metaschema.databind.io.json.IJsonWritingContext; -import gov.nist.secauto.metaschema.databind.io.json.JsonUtil; import gov.nist.secauto.metaschema.databind.io.xml.IXmlParsingContext; import gov.nist.secauto.metaschema.databind.io.xml.IXmlWritingContext; diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/MapPropertyInfo.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/MapPropertyInfo.java index 021130cea..4f441b234 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/MapPropertyInfo.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/MapPropertyInfo.java @@ -30,12 +30,12 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import gov.nist.secauto.metaschema.databind.io.BindingException; import gov.nist.secauto.metaschema.databind.io.json.IJsonParsingContext; import gov.nist.secauto.metaschema.databind.io.json.IJsonWritingContext; -import gov.nist.secauto.metaschema.databind.io.json.JsonUtil; import gov.nist.secauto.metaschema.databind.io.xml.IXmlParsingContext; import gov.nist.secauto.metaschema.databind.io.xml.IXmlWritingContext; diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/FormatDetectorTest.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/FormatDetectorTest.java new file mode 100644 index 000000000..9d0d0e6ae --- /dev/null +++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/FormatDetectorTest.java @@ -0,0 +1,84 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.databind.io; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import gov.nist.secauto.metaschema.databind.test.util.CloseDetectingInputStream; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +class FormatDetectorTest { + + @Test + void testDetectXml() throws IOException { + try (InputStream is = FormatDetectorTest.class.getResourceAsStream( + "/test-content/bound-class-simple.xml")) { + + CloseDetectingInputStream cis = new CloseDetectingInputStream(is); + FormatDetector detector = new FormatDetector(); + FormatDetector.Result result = detector.detect(cis); + + assertAll( + () -> assertNotNull(is), + () -> assertEquals(Format.XML, result.getFormat()), + () -> assertFalse(cis.isClosed(), "primary closed"), + () -> { + result.getDataStream().close(); + assertTrue(cis.isClosed(), "secondary closed"); + }); + } + } + + @Test + void testDetectJson() throws IOException { + try (InputStream is = FormatDetectorTest.class.getResourceAsStream( + "/test-content/bound-class-simple.json")) { + + CloseDetectingInputStream cis = new CloseDetectingInputStream(is); + FormatDetector detector = new FormatDetector(); + FormatDetector.Result result = detector.detect(cis); + + assertAll( + () -> assertNotNull(is), + () -> assertEquals(Format.JSON, result.getFormat()), + () -> assertFalse(cis.isClosed(), "primary closed"), + () -> { + result.getDataStream().close(); + assertTrue(cis.isClosed(), "secondary closed"); + }); + } + } + +} diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/model/DefaultFieldPropertyTest.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/model/DefaultFieldPropertyTest.java index 66ccdf509..63260e9ab 100644 --- a/databind/src/test/java/gov/nist/secauto/metaschema/databind/model/DefaultFieldPropertyTest.java +++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/model/DefaultFieldPropertyTest.java @@ -38,8 +38,8 @@ import com.fasterxml.jackson.core.JsonToken; import gov.nist.secauto.metaschema.core.model.IMetaschema; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.databind.IBindingContext; -import gov.nist.secauto.metaschema.databind.io.json.JsonUtil; import gov.nist.secauto.metaschema.databind.io.json.MetaschemaJsonParser; import gov.nist.secauto.metaschema.databind.model.test.MultiFieldAssembly; import gov.nist.secauto.metaschema.databind.model.test.SimpleAssembly; diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/CloseDetectingInputStream.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/CloseDetectingInputStream.java new file mode 100644 index 000000000..62471ce9e --- /dev/null +++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/CloseDetectingInputStream.java @@ -0,0 +1,130 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.databind.test.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class CloseDetectingInputStream + extends InputStream { + + private final InputStream delegate; + private boolean closed; + + public CloseDetectingInputStream(@NonNull InputStream delegate) { + this.delegate = delegate; + } + + public boolean isClosed() { + return closed; + } + + @Override + public int read() throws IOException { + return delegate.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return delegate.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + @Override + public byte[] readAllBytes() throws IOException { + return delegate.readAllBytes(); + } + + @Override + public byte[] readNBytes(int len) throws IOException { + return delegate.readNBytes(len); + } + + @Override + public int readNBytes(byte[] b, int off, int len) throws IOException { + return delegate.readNBytes(b, off, len); + } + + @Override + public long skip(long n) throws IOException { + return delegate.skip(n); + } + + @Override + public int available() throws IOException { + return delegate.available(); + } + + @Override + public void close() throws IOException { + delegate.close(); + closed = true; + } + + @Override + public synchronized void mark(int readlimit) { + delegate.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + delegate.reset(); + } + + @Override + public boolean markSupported() { + return delegate.markSupported(); + } + + @Override + public long transferTo(OutputStream out) throws IOException { + return delegate.transferTo(out); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public String toString() { + return delegate.toString(); + } + +} diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/package-info.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/package-info.java new file mode 100644 index 000000000..f45c2a147 --- /dev/null +++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/test/util/package-info.java @@ -0,0 +1,27 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.databind.test.util; diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java index c9e10520d..ced64684f 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java @@ -47,6 +47,7 @@ import gov.nist.secauto.metaschema.databind.IBindingContext; import gov.nist.secauto.metaschema.databind.IBindingContext.IValidationSchemaProvider; import gov.nist.secauto.metaschema.databind.io.Format; +import gov.nist.secauto.metaschema.databind.io.FormatDetector; import gov.nist.secauto.metaschema.databind.io.IBoundLoader; import org.apache.commons.cli.CommandLine; @@ -230,8 +231,9 @@ public ExitStatus execute() { } } else { // attempt to determine the format + FormatDetector.Result formatResult; try { - asFormat = loader.detectFormat(source); + formatResult = loader.detectFormat(source); } catch (FileNotFoundException ex) { // this case was already checked for return ExitCode.IO_ERROR.exitMessage("The provided source file '" + source + "' does not exist."); @@ -244,6 +246,7 @@ public ExitStatus execute() { .map(format -> format.name()) .collect(CustomCollectors.joiningWithOxfordComma("or"))); } + asFormat = formatResult.getFormat(); } if (LOGGER.isInfoEnabled()) { diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentWithMetaschemaCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentWithMetaschemaCommand.java index 4c16bee0e..dfdd8e87c 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentWithMetaschemaCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateContentWithMetaschemaCommand.java @@ -34,8 +34,8 @@ import gov.nist.secauto.metaschema.core.model.IMetaschema; import gov.nist.secauto.metaschema.core.model.MetaschemaException; import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet; +import gov.nist.secauto.metaschema.core.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.model.util.XmlUtil; -import gov.nist.secauto.metaschema.core.model.validation.JsonSchemaContentValidator; import gov.nist.secauto.metaschema.core.model.xml.MetaschemaLoader; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -175,7 +175,7 @@ public JSONObject getJsonSchema() throws IOException { IMutableConfiguration> configuration = new DefaultConfiguration<>(); ISchemaGenerator.generateSchema(getMetaschema(), schemaFile, SchemaFormat.JSON, configuration); try (BufferedReader reader = ObjectUtils.notNull(Files.newBufferedReader(schemaFile, StandardCharsets.UTF_8))) { - return JsonSchemaContentValidator.toJsonObject(reader); + return JsonUtil.toJsonObject(reader); } } } diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateMetaschemaCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateMetaschemaCommand.java index e9e333e43..55e35a14b 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateMetaschemaCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/ValidateMetaschemaCommand.java @@ -128,7 +128,9 @@ protected ExitStatus executeCommand(CallingContext callingContext, CommandLine c IValidationResult schemaValidationResult; try { List schemaSources = getXmlSchemaSources(); - schemaValidationResult = IContentValidator.validateWithXmlSchema(target, schemaSources); + schemaValidationResult = IContentValidator.validateWithXmlSchema( + ObjectUtils.notNull(target.toUri()), + schemaSources); } catch (IOException | SAXException ex) { return ExitCode.PROCESSING_ERROR.exit().withThrowable(ex); } diff --git a/pom.xml b/pom.xml index d1277ca9b..195652105 100644 --- a/pom.xml +++ b/pom.xml @@ -1,288 +1,295 @@ - - 4.0.0 - - gov.nist.secauto - oss-parent - 26 - - gov.nist.secauto.metaschema - metaschema-framework - 1.0.0-SNAPSHOT - pom - Metaschema Framework Parent - A common build root for all NIST Java projects related to the - Metaschema framework. - ${site.url} - - https://github.com/usnistgov/metaschema-java/issues - GitHub Issues - - - https://github.com/usnistgov/metaschema-java/tree/main - scm:git:git@github.com/usnistgov/metaschema-java.git - scm:git:git@github.com:usnistgov/metaschema-java.git - v0.12.0 - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - nist-pages - ${site.url} - - - - - NIST License - https://www.nist.gov/director/licensing - repo - NIST software License - - - - National Institute of Standards and Technology - https://www.nist.gov - - - - david.waltermire@nist.gov - David Waltermire - david.waltermire@nist.gov - National Institute of Standards and Technology - - architect - developer - maintainer - - - - - - OSCAL NIST Team - oscal@nist.gov - - - OSCAL Project Development Discussion - oscal-dev@nist.gov - oscal-dev-join@nist.gov - oscal-dev-leave@nist.gov - - - OSCAL Project Updates - oscal-updates-join@nist.gov - oscal-updates-leave@nist.gov - - - - - https://pages.nist.gov/metaschema-java/ - https://github.com/usnistgov/metaschema-java - https://github.com/usnistgov/metaschema-java/issues - - - - 3.2.2 - 4.4.1 - 4.4.1 - 9.15.7 - github - 3.4.1 - 5.12.1 - 1.16.0 + + 4.0.0 + + gov.nist.secauto + oss-parent + 26 + + gov.nist.secauto.metaschema + metaschema-framework + 1.0.0-SNAPSHOT + pom + Metaschema Framework Parent + A common build root for all NIST Java projects related to the + Metaschema framework. + ${site.url} + + https://github.com/usnistgov/metaschema-java/issues + GitHub Issues + + + https://github.com/usnistgov/metaschema-java/tree/main + scm:git:git@github.com/usnistgov/metaschema-java.git + + scm:git:git@github.com:usnistgov/metaschema-java.git + v0.12.0 + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + nist-pages + ${site.url} + + + + + NIST License + https://www.nist.gov/director/licensing + repo + NIST software License + + + + National Institute of Standards and Technology + https://www.nist.gov + + + + david.waltermire@nist.gov + David Waltermire + david.waltermire@nist.gov + National Institute of Standards and Technology + + architect + developer + maintainer + + + + + + OSCAL NIST Team + oscal@nist.gov + + + OSCAL Project Development Discussion + oscal-dev@nist.gov + oscal-dev-join@nist.gov + oscal-dev-leave@nist.gov + + + OSCAL Project Updates + oscal-updates-join@nist.gov + oscal-updates-leave@nist.gov + + + + + https://pages.nist.gov/metaschema-java/ + https://github.com/usnistgov/metaschema-java + https://github.com/usnistgov/metaschema-java/issues + + + + 3.2.2 + 4.4.1 + 4.4.1 + 9.15.7 + github + 3.4.1 + 5.12.1 + 1.16.0 - 11 - 11 - 11 - ${maven.compiler.target} + 11 + 11 + 11 + ${maven.compiler.target} - UTF-8 - UTF-8 - UTF-8 + UTF-8 + UTF-8 + UTF-8 - 3.9.3 + 3.9.3 - ${maven.build.timestamp} - yyyy-MM-dd'T'HH:mm:ssZ + ${maven.build.timestamp} + yyyy-MM-dd'T'HH:mm:ssZ - 4.10.1 - 1.1.1 - 1.5.0 - 4.4 - 2.13.0 - 3.12.0 - 1.10.0 - 1.14.2 - 0.64.8 - 2.3.32 - 0.3.3 - 2.15.2 - 2.15.2 - 2.4.0 - 4.0.0 - 2.3.1 - 2.0.0 - 2.0.6.1 - 2.12.0 - 5.9.0 - 1.9.0 - 2.20.0 - 3.0.3 - 4.0.0 - 1.2.0 - 12.3 - 4.7.3 - 4.2.1 - 3.0.15.RELEASE - 6.5.1 - 5.1.1 - 1.5.6-120 - 5.2.0 - 2.0.2 + 4.10.1 + 1.1.1 + 1.5.0 + 4.4 + 2.13.0 + 3.12.0 + 1.10.0 + 1.14.2 + 0.64.8 + 2.3.32 + 0.3.3 + 2.15.2 + 2.15.2 + 2.4.0 + 4.0.0 + 2.3.1 + 2.0.0 + 2.0.6.1 + 2.12.0 + 5.9.0 + 1.9.0 + 2.20.0 + 3.0.3 + 4.0.0 + 1.2.0 + 12.3 + 4.7.3 + 4.2.1 + 3.0.15.RELEASE + 6.5.1 + 5.1.1 + 1.5.6-120 + 5.2.0 + 2.0.2 - 1.22 - 2.1.0 - 6.0.0 - 2.12.1 - 3.6.0 - 3.1.0 - 1.0.0 - - - - - - - Nexus Snapshots - snapshots-repo - https://oss.sonatype.org/content/repositories/snapshots - default - - false - - - true - always - fail - - - - - - apache.snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - - - - - - - ${project.groupId} - metaschema-core - ${project.version} - - - ${project.groupId} - metaschema-databind - ${project.version} - - - ${project.groupId} - metaschema-freemarker-support - ${project.version} - - - ${project.groupId} - metaschema-schema-generator - ${project.version} - - - ${project.groupId} - metaschema-testing - ${project.version} - - - ${project.groupId} - cli-processor - ${project.version} - - - com.google.auto.service - auto-service-annotations - ${dependency.auto-service.version} - provided - - - org.apache.xmlbeans - xmlbeans - ${dependency.xmlbeans.version} - - - - org.apache.logging.log4j - log4j-api - - - - - - com.github.javaparser - javaparser-symbol-solver-core - 3.24.4 - - - org.jdom - jdom2 - ${dependency.jdom2.version} - - - - jaxen - jaxen - ${dependency.jaxen.version} - - - org.eclipse.persistence - org.eclipse.persistence.moxy - ${dependency.moxy.version} - - - javax.xml.bind - jaxb-api - ${dependency.jaxb-api.version} - - - com.sun.xml.bind - jaxb-impl - ${dependency.jaxb.version} - - - org.codehaus.woodstox - stax2-api - ${dependency.stax2-api.version} - - - net.sf.saxon - Saxon-HE - ${dependency.saxon.version} - + org.apache.logging.log4j + log4j-api + + + + + + com.github.javaparser + javaparser-symbol-solver-core + 3.24.4 + + + org.jdom + jdom2 + ${dependency.jdom2.version} + + + + jaxen + jaxen + ${dependency.jaxen.version} + + + org.eclipse.persistence + org.eclipse.persistence.moxy + ${dependency.moxy.version} + + + javax.xml.bind + jaxb-api + ${dependency.jaxb-api.version} + + + com.sun.xml.bind + jaxb-impl + ${dependency.jaxb.version} + + + org.codehaus.woodstox + stax2-api + ${dependency.stax2-api.version} + + + net.sf.saxon + Saxon-HE + ${dependency.saxon.version} + - - - org.xmlresolver - xmlresolver - ${dependency.xmlresolver.version} - - - xml-apis - xml-apis - - - - - - org.xmlresolver - xmlresolver - data - ${dependency.xmlresolver.version} - - - xml-apis - xml-apis - - - - - xml-apis - xml-apis - ${dependency.xml-apis.version} - - - com.xmlcalabash - xmlcalabash - ${dependency.xmlcalabash.version} - - - com.vladsch.flexmark - flexmark - ${dependency.flexmark.version} - - - com.vladsch.flexmark - flexmark-ext-tables - ${dependency.flexmark.version} - - - com.vladsch.flexmark - flexmark-ext-escaped-character - ${dependency.flexmark.version} - - - com.vladsch.flexmark - flexmark-ext-superscript - ${dependency.flexmark.version} - - - com.vladsch.flexmark - flexmark-ext-gfm-strikethrough - ${dependency.flexmark.version} - - - com.vladsch.flexmark - flexmark-ext-typographic - ${dependency.flexmark.version} - - - - - - - - com.vladsch.flexmark - flexmark-html2md-converter - ${dependency.flexmark.version} - - - com.fasterxml.woodstox - woodstox-core - ${dependency.woodstox.version} - - - com.fasterxml.jackson.core - jackson-core - ${dependency.jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - ${dependency.jackson-databind.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - ${dependency.jackson.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${dependency.jackson.version} - - - com.github.erosb - everit-json-schema - ${dependency.everit-json.version} - - - org.freemarker - freemarker - ${dependency.freemarker.version} - - - org.thymeleaf - thymeleaf - ${dependency.thymeleaf.version} - - - org.antlr - antlr4-runtime - ${dependency.antlr4.version} - - - nl.talsmasoftware - lazy4j - 1.0.2 - - - com.github.seancfoley - ipaddress - 5.4.0 - - - org.fusesource.jansi - jansi - ${dependency.jansi.version} - - - org.apache.logging.log4j - log4j-core - ${dependency.log4j2.version} - - - org.apache.logging.log4j - log4j-jul - ${dependency.log4j2.version} - - - - org.apache.logging.log4j - log4j-slf4j-impl - ${dependency.log4j2.version} - - - - org.apache.logging.log4j - log4j-api - ${dependency.log4j2.version} - - - org.apache.commons - commons-lang3 - ${dependency.commons-lang3.version} - - - org.apache.commons - commons-collections4 - ${dependency.commons-collections4.version} - - - org.apache.commons - commons-text - ${dependency.commons-text.version} - - - commons-cli - commons-cli - ${dependency.commons-cli.version} - - - commons-io - commons-io - ${dependency.commons-io.version} - - - com.squareup - javapoet - 1.13.0 - - - net.openhft - compiler - 2.23ea0 - - - com.github.spotbugs - spotbugs-annotations - ${dependency.spotbugs-annotations.version} - provided - - - org.junit.jupiter - junit-jupiter - ${dependency.junit5.version} - test - - - org.junit.jupiter - junit-jupiter-api - ${dependency.junit5.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${dependency.junit5.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${dependency.junit5.version} - test - - - org.junit.platform - junit-platform-launcher - ${dependency.junit5-platform-launcher.version} - test - - - org.jmock - jmock-junit5 - ${dependency.jmock-junit5.version} - test - - - com.google.code.findbugs - annotations - - - - - io.hosuaby - inject-resources-junit-jupiter - ${dependency.inject-resources-junit-jupiter.version} - test - - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.9.0 - - - - biz.aQute.bnd - biz.aQute.bndlib - 6.4.1 - - - biz.aQute.bnd - biz.aQute.bnd.util - 6.4.1 - - - - - - com.github.spotbugs - spotbugs-annotations - - - - org.junit.jupiter - junit-jupiter-api - - - - - - - - - - - org.apache.maven.plugins - maven-toolchains-plugin - ${plugin.maven-toolchains.version} - - - - toolchain - - - - - - - 11 - temurin - - - - - + + + org.xmlresolver + xmlresolver + ${dependency.xmlresolver.version} + + + xml-apis + xml-apis + + + + + + org.xmlresolver + xmlresolver + data + ${dependency.xmlresolver.version} + + + xml-apis + xml-apis + + + + + xml-apis + xml-apis + ${dependency.xml-apis.version} + + + com.xmlcalabash + xmlcalabash + ${dependency.xmlcalabash.version} + + + com.vladsch.flexmark + flexmark + ${dependency.flexmark.version} + + + com.vladsch.flexmark + flexmark-ext-tables + ${dependency.flexmark.version} + + + com.vladsch.flexmark + flexmark-ext-escaped-character + ${dependency.flexmark.version} + + + com.vladsch.flexmark + flexmark-ext-superscript + ${dependency.flexmark.version} + + + com.vladsch.flexmark + flexmark-ext-gfm-strikethrough + ${dependency.flexmark.version} + + + com.vladsch.flexmark + flexmark-ext-typographic + ${dependency.flexmark.version} + + + + + + + + com.vladsch.flexmark + flexmark-html2md-converter + ${dependency.flexmark.version} + + + com.fasterxml.woodstox + woodstox-core + ${dependency.woodstox.version} + + + com.fasterxml.jackson.core + jackson-core + ${dependency.jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${dependency.jackson-databind.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + ${dependency.jackson.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${dependency.jackson.version} + + + com.github.erosb + everit-json-schema + ${dependency.everit-json.version} + + + org.freemarker + freemarker + ${dependency.freemarker.version} + + + org.thymeleaf + thymeleaf + ${dependency.thymeleaf.version} + + + org.antlr + antlr4-runtime + ${dependency.antlr4.version} + + + nl.talsmasoftware + lazy4j + 1.0.2 + + + com.github.seancfoley + ipaddress + 5.4.0 + + + org.fusesource.jansi + jansi + ${dependency.jansi.version} + + + org.apache.logging.log4j + log4j-core + ${dependency.log4j2.version} + + + org.apache.logging.log4j + log4j-jul + ${dependency.log4j2.version} + + + + org.apache.logging.log4j + log4j-slf4j-impl + ${dependency.log4j2.version} + + + + org.apache.logging.log4j + log4j-api + ${dependency.log4j2.version} + + + org.apache.commons + commons-lang3 + ${dependency.commons-lang3.version} + + + org.apache.commons + commons-collections4 + ${dependency.commons-collections4.version} + + + org.apache.commons + commons-text + ${dependency.commons-text.version} + + + commons-cli + commons-cli + ${dependency.commons-cli.version} + + + commons-io + commons-io + ${dependency.commons-io.version} + + + com.squareup + javapoet + 1.13.0 + + + net.openhft + compiler + 2.23ea0 + + + com.github.spotbugs + spotbugs-annotations + ${dependency.spotbugs-annotations.version} + + + org.jmock + jmock-junit5 + ${dependency.jmock-junit5.version} + test + + + com.google.code.findbugs + annotations + + + + + io.hosuaby + inject-resources-junit-jupiter + ${dependency.inject-resources-junit-jupiter.version} + test + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.9.0 + + + + biz.aQute.bnd + biz.aQute.bndlib + 6.4.1 + + + biz.aQute.bnd + biz.aQute.bnd.util + 6.4.1 + + + + + + com.github.spotbugs + spotbugs-annotations + + + + org.junit.jupiter + junit-jupiter-engine + + + + + + + org.apache.maven.plugins + maven-toolchains-plugin + ${plugin.maven-toolchains.version} + + + + toolchain + + + + + + + 11 + temurin + + + + + org.apache.maven.plugins maven-compiler-plugin - - - - com.google.auto.service - auto-service - ${dependency.auto-service.version} - - - + + + + com.google.auto.service + auto-service + ${dependency.auto-service.version} + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + com.google.auto.service:auto-service + + + org.apache.logging.log4j:log4j-core + + - - org.apache.maven.plugins - maven-dependency-plugin - - - com.google.auto.service:auto-service - - - org.apache.logging.log4j:log4j-core - - - - - gov.nist.secauto - oss-build-support - 26 - - - - - true - org.apache.xmlbeans.metadata - ALL,-XGET,-XSET,-XGET_ARRAY,-XSET_ARRAY,-XGET_IDX,-XSET_IDX - - - - org.antlr - antlr4-maven-plugin - ${dependency.antlr4.version} - - - com.khubla.antlr - antlr4test-maven-plugin - ${plugin.antlr4test.version} - - - org.apache.maven.plugins - maven-site-plugin - - ${site.url} - - - - org.apache.maven.plugins - maven-javadoc-plugin - + + gov.nist.secauto + oss-build-support + 26 + + + + + true + org.apache.xmlbeans.metadata + + ALL,-XGET,-XSET,-XGET_ARRAY,-XSET_ARRAY,-XGET_IDX,-XSET_IDX + + + + org.antlr + antlr4-maven-plugin + ${dependency.antlr4.version} + + + com.khubla.antlr + antlr4test-maven-plugin + ${plugin.antlr4test.version} + + + org.apache.maven.plugins + maven-site-plugin + + ${site.url} + + + + org.apache.maven.plugins + maven-javadoc-plugin + *.xmlbeans:*.xmlbeans.*:*.antlr - false - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - + false + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + org.apache.maven.plugins maven-checkstyle-plugin ${project.build.sourceDirectory} - ${project.build.testSourceDirectory} + + ${project.build.testSourceDirectory} - - com.github.spotbugs - spotbugs-maven-plugin - - spotbugs-exclude.xml - - - - org.apache.maven.plugins - maven-invoker-plugin - ${plugin.maven-invoker.version} - - - io.github.git-commit-id - git-commit-id-maven-plugin - ${plugin.git-commit-id.version} - - ${project.basedir}/.git - false - false - - - - org.codehaus.mojo - templating-maven-plugin - ${plugin.templating.version} - - - org.codehaus.mojo - appassembler-maven-plugin - ${plugin.appassembler.version} - - - org.apache.maven.plugins - maven-surefire-plugin - - 1.5C - true - @{argLine} -Xmx1024m - - JAVA_TOOL_OPTIONS - - - - - - - - - - org.apache.maven.plugins - maven-toolchains-plugin - - - - - - reporting - - - - - org.apache.maven.plugins - maven-pmd-plugin - - - pmd-verify - - check - - - 2 - - true - true - sarif - - - - - - com.github.spotbugs - spotbugs-maven-plugin - - true - - - - - - - org.apache.maven.plugins - maven-site-plugin - - - - attach-descriptor - false - - attach-descriptor - - - - - - - - - - core - databind - metaschema-maven-plugin - metaschema-schema-generator - metaschema-testing - cli-processor - metaschema-cli - - + + com.github.spotbugs + spotbugs-maven-plugin + + spotbugs-exclude.xml + + + + org.apache.maven.plugins + maven-invoker-plugin + ${plugin.maven-invoker.version} + + + io.github.git-commit-id + git-commit-id-maven-plugin + ${plugin.git-commit-id.version} + + ${project.basedir}/.git + false + false + + + + org.codehaus.mojo + templating-maven-plugin + ${plugin.templating.version} + + + org.codehaus.mojo + appassembler-maven-plugin + ${plugin.appassembler.version} + + + org.apache.maven.plugins + maven-surefire-plugin + + 1.5C + true + @{argLine} -Xmx1024m + + JAVA_TOOL_OPTIONS + + + + + + + + + + org.apache.maven.plugins + maven-toolchains-plugin + + + + + + reporting + + + + + org.apache.maven.plugins + maven-pmd-plugin + + + pmd-verify + + check + + + 2 + + true + true + sarif + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + true + + + + + + + org.apache.maven.plugins + maven-site-plugin + + + + attach-descriptor + false + + attach-descriptor + + + + + + + + + + core + databind + metaschema-maven-plugin + metaschema-schema-generator + metaschema-testing + cli-processor + metaschema-cli + + - - metaschema-model-common - metaschema-model - metaschema-java-codegen - metaschema-java-binding - + + metaschema-model-common + metaschema-model + metaschema-java-codegen + metaschema-java-binding + \ No newline at end of file