diff --git a/core/src/main/antlr4/Metapath10.g4 b/core/src/main/antlr4/Metapath10.g4 index ecfb5f43d..1b25cc13f 100644 --- a/core/src/main/antlr4/Metapath10.g4 +++ b/core/src/main/antlr4/Metapath10.g4 @@ -148,7 +148,7 @@ parenthesizeditemtype : OP itemtype CP ; // Error in the spec. EQName also includes acceptable keywords. -eqname : NCName | QName | URIQualifiedName +eqname : URIQualifiedName | NCName | QName | KW_ANCESTOR | KW_ANCESTOR_OR_SELF | KW_AND diff --git a/core/src/main/antlr4/Metapath10Lexer.g4 b/core/src/main/antlr4/Metapath10Lexer.g4 index 43ebf1b40..879194d59 100644 --- a/core/src/main/antlr4/Metapath10Lexer.g4 +++ b/core/src/main/antlr4/Metapath10Lexer.g4 @@ -117,7 +117,7 @@ DecimalLiteral : '.' FragDigits | FragDigits '.' [0-9]*; DoubleLiteral : ('.' FragDigits | FragDigits ('.' [0-9]*)?) [eE] [+-]? FragDigits; StringLiteral : '"' (~["] | FragEscapeQuot)* '"' | '\'' (~['] | FragEscapeApos)* '\''; URIQualifiedName : BracedURILiteral NCName; -BracedURILiteral : 'Q' '{' [^{}]* '}'; +BracedURILiteral : 'Q' '{' ~[{}]* '}'; // Error in spec: EscapeQuot and EscapeApos are not terminals! fragment FragEscapeQuot : '""'; fragment FragEscapeApos : '\'\''; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMAssemblyNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMAssemblyNodeItem.java index 09cf2017c..ee85de9d4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMAssemblyNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMAssemblyNodeItem.java @@ -5,30 +5,54 @@ package gov.nist.secauto.metaschema.core.mdm; +import gov.nist.secauto.metaschema.core.mdm.impl.DefinitionAssemblyNodeItem; import gov.nist.secauto.metaschema.core.mdm.impl.IDMModelNodeItem; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; import gov.nist.secauto.metaschema.core.model.IFieldInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import edu.umd.cs.findbugs.annotations.NonNull; /** - * An assembly node item implementation that is backed by a simple Metaschema - * module-based data model. + * Represents a Metapath assembly node item that is backed by a simple + * Metaschema module-based data model. + *

+ * The {@link #newInstance(IAssemblyDefinition, StaticContext)} method can be + * used to create a node from an {@link IAssemblyDefinition} that is orphaned + * from a document model. + *

+ * Child nodes can be created using the + * {@link #newFlag(gov.nist.secauto.metaschema.core.model.IFlagInstance, IAnyAtomicItem)}, + * {@link #newAssembly(IAssemblyInstance)}, and + * {@link #newField(IFieldInstance, IAnyAtomicItem)} methods. These children are + * added to this assembly. */ public interface IDMAssemblyNodeItem extends IAssemblyNodeItem, IDMModelNodeItem { + /** + * Create new assembly node item that is detached from a parent node item. + * + * @param definition + * the Metaschema field definition describing the assembly + * @param staticContext + * the atomic field value + * @return the new field node item + */ + @NonNull + static IDMAssemblyNodeItem newInstance( + @NonNull IAssemblyDefinition definition, + @NonNull StaticContext staticContext) { + return new DefinitionAssemblyNodeItem(definition, staticContext); + } + /** * Create and add a new field to the underlying data model. * * @param instance * the Metaschema field instance describing the field - * @param resourceLocation - * information about the location of the field within the containing - * resource * @param value * the atomic field value * @return the new field node item @@ -36,7 +60,6 @@ public interface IDMAssemblyNodeItem @NonNull IDMFieldNodeItem newField( @NonNull IFieldInstance instance, - @NonNull IResourceLocation resourceLocation, @NonNull IAnyAtomicItem value); /** @@ -44,13 +67,9 @@ IDMFieldNodeItem newField( * * @param instance * the Metaschema assembly instance describing the assembly - * @param resourceLocation - * information about the location of the assembly within the containing - * resource * @return the new assembly node item */ @NonNull IDMAssemblyNodeItem newAssembly( - @NonNull IAssemblyInstance instance, - @NonNull IResourceLocation resourceLocation); + @NonNull IAssemblyInstance instance); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMDocumentNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMDocumentNodeItem.java index 275b5a341..3bd5fa672 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMDocumentNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMDocumentNodeItem.java @@ -5,18 +5,20 @@ package gov.nist.secauto.metaschema.core.mdm; -import gov.nist.secauto.metaschema.core.mdm.impl.DocumentImpl; +import gov.nist.secauto.metaschema.core.mdm.impl.DocumentNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; /** - * A document node item implementation that is backed by a simple Metaschema - * module-based data model. + * Represents a Metapath document node item that is backed by a simple + * Metaschema module-based data model. + *

+ * The {@link #newInstance(URI, IAssemblyDefinition)} method can be used to + * create a new document-based data model. */ public interface IDMDocumentNodeItem extends IDocumentNodeItem { @@ -28,21 +30,14 @@ public interface IDMDocumentNodeItem * * @param resource * the base URI of the document resource - * @param resourceLocation - * information about the (intended) location of the document resource * @param rootAssembly * the assembly that is at the root of the node tree for this document - * @param rootAssemblyLocation - * information about the (intended) location of the root assembly - * resource * @return the document node item */ @NonNull static IDMDocumentNodeItem newInstance( @NonNull URI resource, - @NonNull IResourceLocation resourceLocation, - @NonNull IAssemblyDefinition rootAssembly, - @NonNull IResourceLocation rootAssemblyLocation) { - return new DocumentImpl(resource, resourceLocation, rootAssembly, rootAssemblyLocation); + @NonNull IAssemblyDefinition rootAssembly) { + return new DocumentNodeItem(resource, rootAssembly); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFieldNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFieldNodeItem.java index f26e3680f..504f407eb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFieldNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFieldNodeItem.java @@ -5,16 +5,44 @@ package gov.nist.secauto.metaschema.core.mdm; +import gov.nist.secauto.metaschema.core.mdm.impl.DefinitionFieldNodeItem; import gov.nist.secauto.metaschema.core.mdm.impl.IDMModelNodeItem; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IAtomicValuedNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IFieldNodeItem; +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; import gov.nist.secauto.metaschema.core.model.IFieldInstance; +import edu.umd.cs.findbugs.annotations.NonNull; + /** - * A field node item implementation that is backed by a simple Metaschema + * Represents a Metapath field node item that is backed by a simple Metaschema * module-based data model. + *

+ * The {@link #newInstance(IFieldDefinition, IAnyAtomicItem, StaticContext)} + * method can be used to create a node from an {@link IAssemblyDefinition} that + * is orphaned from a document model. */ public interface IDMFieldNodeItem - extends IFieldNodeItem, IDMModelNodeItem { - // no additional methods + extends IFieldNodeItem, IDMModelNodeItem, IAtomicValuedNodeItem, IDMNodeItem { + /** + * Create new field node item that is detached from a parent node item. + * + * @param definition + * the Metaschema field definition describing the field + * @param value + * the field's initial value + * @param staticContext + * the atomic field value + * @return the new field node item + */ + @NonNull + static IDMFieldNodeItem newInstance( + @NonNull IFieldDefinition definition, + @NonNull IAnyAtomicItem value, + @NonNull StaticContext staticContext) { + return new DefinitionFieldNodeItem(definition, value, staticContext); + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFlagNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFlagNodeItem.java new file mode 100644 index 000000000..455ed5c60 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMFlagNodeItem.java @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm; + +import gov.nist.secauto.metaschema.core.metapath.item.node.IAtomicValuedNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; + +/** + * Represents a Metapath flag node item that is backed by a simple Metaschema + * module-based data model. + */ +public interface IDMFlagNodeItem extends IFlagNodeItem, IDMNodeItem, IAtomicValuedNodeItem { + // no additional methods +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMNodeItem.java new file mode 100644 index 000000000..269fd4d2d --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMNodeItem.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm; + +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.model.IResourceLocation; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a Metapath node item that is backed by a simple Metaschema + * module-based data model. + */ +public interface IDMNodeItem extends INodeItem { + /** + * Provides a means to change the location information for the node item. + * + * @param location + * information about the location of the node within the containing + * resource + */ + void setLocation(@NonNull IResourceLocation location); +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMRootAssemblyNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMRootAssemblyNodeItem.java index 03cab04ce..13854b75a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMRootAssemblyNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/IDMRootAssemblyNodeItem.java @@ -8,8 +8,8 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IRootAssemblyNodeItem; /** - * A root assembly node item implementation that is backed by a simple - * Metaschema module-based data model. + * Represents a Metapath root assembly node item implementation that is backed + * by a simple Metaschema module-based data model. */ public interface IDMRootAssemblyNodeItem extends IDMAssemblyNodeItem, IRootAssemblyNodeItem { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMAssemblyNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMAssemblyNodeItem.java index 6eda28488..7be95ecb6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMAssemblyNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMAssemblyNodeItem.java @@ -8,12 +8,9 @@ import gov.nist.secauto.metaschema.core.mdm.IDMAssemblyNodeItem; import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.AbstractNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; import gov.nist.secauto.metaschema.core.model.IFieldInstance; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -27,22 +24,22 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * This abstract Metapath assmebly node item implementation supports creating a + * Metaschema module-based data model. + */ public abstract class AbstractDMAssemblyNodeItem - extends AbstractNodeItem + extends AbstractDMModelNodeItem implements IDMAssemblyNodeItem { @NonNull - private final Map flags = new ConcurrentHashMap<>(); - @NonNull private final Map>> modelItems = new ConcurrentHashMap<>(); + /** + * Construct a new node item. + */ protected AbstractDMAssemblyNodeItem() { - // nothing to do - } - - @Override - public Object getValue() { - return this; + // only allow extending classes to create instances } @Override @@ -55,26 +52,6 @@ protected String getValueSignature() { return ""; } - @Override - public Collection getFlags() { - return ObjectUtils.notNull(flags.values()); - } - - @Override - public IFlagNodeItem getFlagByName(IEnhancedQName name) { - return flags.get(name); - } - - @Override - public IFlagNodeItem newFlag( - @NonNull IFlagInstance instance, - @NonNull IResourceLocation resourceLocation, - @NonNull IAnyAtomicItem value) { - IFlagNodeItem flag = new FlagImpl(instance, this, resourceLocation, value); - flags.put(instance.getQName(), flag); - return flag; - } - @Override public Collection>> getModelItems() { return ObjectUtils.notNull(modelItems.values()); @@ -87,21 +64,21 @@ public IFlagNodeItem newFlag( } @Override - public IDMFieldNodeItem newField(IFieldInstance instance, IResourceLocation resourceLocation, IAnyAtomicItem value) { + public IDMFieldNodeItem newField(IFieldInstance instance, IAnyAtomicItem value) { List> result = modelItems.computeIfAbsent( instance.getQName(), - name -> Collections.synchronizedList(new LinkedList>())); - IDMFieldNodeItem field = new FieldImpl(instance, this, resourceLocation, value); + name -> Collections.synchronizedList(new LinkedList<>())); + IDMFieldNodeItem field = new ChildFieldNodeItem(instance, this, value); result.add(field); return field; } @Override - public IDMAssemblyNodeItem newAssembly(IAssemblyInstance instance, IResourceLocation resourceLocation) { + public IDMAssemblyNodeItem newAssembly(IAssemblyInstance instance) { List> result = modelItems.computeIfAbsent( instance.getQName(), - name -> Collections.synchronizedList(new LinkedList>())); - IDMAssemblyNodeItem assembly = new AssemblyImpl(instance, this, resourceLocation); + name -> Collections.synchronizedList(new LinkedList<>())); + IDMAssemblyNodeItem assembly = new ChildAssemblyNodeItem(instance, this); result.add(assembly); return assembly; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FieldImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFieldNodeItem.java similarity index 50% rename from core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FieldImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFieldNodeItem.java index 329efcfc9..6468b84b0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FieldImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFieldNodeItem.java @@ -7,38 +7,35 @@ import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; import gov.nist.secauto.metaschema.core.model.IFieldInstance; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.util.CollectionUtil; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import edu.umd.cs.findbugs.annotations.NonNull; -public class FieldImpl - extends AbstractDMInstanceNodeItem +/** + * This abstract Metapath field node item implementation supports creating a + * Metaschema module-based data model. + */ +public abstract class AbstractDMFieldNodeItem + extends AbstractDMModelNodeItem implements IDMFieldNodeItem { @NonNull private IAnyAtomicItem value; - @NonNull - private final Map flags = new ConcurrentHashMap<>(); - public FieldImpl( - @NonNull IFieldInstance instance, - @NonNull IAssemblyNodeItem parent, - @NonNull IResourceLocation resourceLocation, - @NonNull IAnyAtomicItem value) { - super(instance, parent, resourceLocation); + /** + * Construct a new node item. + * + * @param value + * the initial field value + */ + protected AbstractDMFieldNodeItem(@NonNull IAnyAtomicItem value) { + // only allow extending classes to create instances this.value = value; } @@ -47,19 +44,29 @@ public IAnyAtomicItem toAtomicItem() { return value; } + /** + * Change the field's value to the provided value. + * + * @param value + * the new field value + */ public void setValue(@NonNull IAnyAtomicItem value) { this.value = getValueItemType().cast(value); } + /** + * Change the field's value to the provided value. + *

+ * This method expects the provided value to align with the object type + * supported by the underlying atomic type. + * + * @param value + * the new field value + */ public void setValue(@NonNull Object value) { this.value = getValueItemType().newItem(value); } - @Override - public Object getValue() { - return toAtomicItem().getValue(); - } - @Override public String stringValue() { return toAtomicItem().asString(); @@ -70,21 +77,6 @@ protected String getValueSignature() { return toAtomicItem().toSignature(); } - @Override - public int getPosition() { - return getParentNodeItem().getModelItemsByName(getQName()).indexOf(this); - } - - @Override - public Collection getFlags() { - return ObjectUtils.notNull(flags.values()); - } - - @Override - public IFlagNodeItem getFlagByName(IEnhancedQName name) { - return flags.get(name); - } - @Override public Collection>> getModelItems() { // no model items @@ -97,13 +89,4 @@ public IFlagNodeItem getFlagByName(IEnhancedQName name) { return CollectionUtil.emptyList(); } - @Override - public IFlagNodeItem newFlag( - @NonNull IFlagInstance instance, - @NonNull IResourceLocation resourceLocation, - @NonNull IAnyAtomicItem value) { - IFlagNodeItem flag = new FlagImpl(instance, this, resourceLocation, value); - flags.put(instance.getQName(), flag); - return flag; - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFlagNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFlagNodeItem.java new file mode 100644 index 000000000..7b8adca2d --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMFlagNodeItem.java @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.mdm.IDMFlagNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; + +import java.util.Collection; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * This abstract Metapath flag node item implementation supports creating a + * Metaschema module-based data model. + */ +public abstract class AbstractDMFlagNodeItem + extends AbstractDMNodeItem + implements IDMFlagNodeItem { + @NonNull + private IAnyAtomicItem value; + + /** + * Construct a new node item. + * + * @param value + * the initial field value + */ + protected AbstractDMFlagNodeItem( + @NonNull IAnyAtomicItem value) { + // only allow extending classes to create instances + this.value = value; + } + + @Override + public IAnyAtomicItem toAtomicItem() { + return value; + } + + /** + * Change the field's value to the provided value. + * + * @param value + * the new field value + */ + public void setValue(@NonNull IAnyAtomicItem value) { + this.value = getValueItemType().cast(value); + } + + /** + * Change the field's value to the provided value. + *

+ * This method expects the provided value to align with the object type + * supported by the underlying atomic type. + * + * @param value + * the new field value + */ + public void setValue(@NonNull Object value) { + this.value = getValueItemType().newItem(value); + } + + @Override + public String stringValue() { + return toAtomicItem().asString(); + } + + @Override + protected String getValueSignature() { + return toAtomicItem().toSignature(); + } + + @Override + public Collection>> getModelItems() { + // no model items + return CollectionUtil.emptyList(); + } + + @Override + public List> getModelItemsByName(IEnhancedQName name) { + // no model items + return CollectionUtil.emptyList(); + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMInstanceNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMInstanceNodeItem.java deleted file mode 100644 index 6d372c501..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMInstanceNodeItem.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.mdm.impl; - -import gov.nist.secauto.metaschema.core.metapath.StaticContext; -import gov.nist.secauto.metaschema.core.metapath.item.node.AbstractInstanceNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; -import gov.nist.secauto.metaschema.core.model.IDefinition; -import gov.nist.secauto.metaschema.core.model.IModelDefinition; -import gov.nist.secauto.metaschema.core.model.INamedInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public abstract class AbstractDMInstanceNodeItem< - D extends IDefinition, - I extends INamedInstance, - P extends IModelNodeItem> - extends AbstractInstanceNodeItem - implements INodeItem { - @NonNull - private final IResourceLocation resourceLocation; - - protected AbstractDMInstanceNodeItem( - @NonNull I instance, - @NonNull P parent, - @NonNull IResourceLocation resourceLocation) { - super(instance, parent); - this.resourceLocation = resourceLocation; - } - - @Override - public IResourceLocation getLocation() { - return resourceLocation; - } - - @Override - public StaticContext getStaticContext() { - return getParentNodeItem().getStaticContext(); - } -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMModelNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMModelNodeItem.java new file mode 100644 index 000000000..3c34dfcec --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMModelNodeItem.java @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.mdm.IDMFlagNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.model.IModelDefinition; +import gov.nist.secauto.metaschema.core.model.INamedModelInstance; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * This abstract Metapath node item implementation supports creating a + * Metaschema module-based data model that supports child flags. + * + * @param + * the Java type of the definition associated with a Metaschema module + * @param + * the Java type of the instance associated with a Metaschema module + */ +public abstract class AbstractDMModelNodeItem + extends AbstractDMNodeItem + implements IDMModelNodeItem { + @NonNull + private final Map flags = new ConcurrentHashMap<>(); + + /** + * Construct a new node item. + */ + protected AbstractDMModelNodeItem() { + // only allow extending classes to create instances + } + + @Override + public Object getValue() { + return this; + } + + @Override + public Collection getFlags() { + return ObjectUtils.notNull(flags.values()); + } + + @Override + public IDMFlagNodeItem getFlagByName(IEnhancedQName name) { + return flags.get(name); + } + + @Override + public IDMFlagNodeItem newFlag( + @NonNull IFlagInstance instance, + @NonNull IAnyAtomicItem value) { + IDMFlagNodeItem flag = new ChildFlagNodeItem(instance, this, value); + flags.put(instance.getQName(), flag); + return flag; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMNodeItem.java new file mode 100644 index 000000000..8fd526db7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AbstractDMNodeItem.java @@ -0,0 +1,40 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.mdm.IDMNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.AbstractNodeItem; +import gov.nist.secauto.metaschema.core.model.IResourceLocation; + +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * This abstract Metapath assmebly node item implementation supports creating a + * Metaschema module-based data model. + */ +public abstract class AbstractDMNodeItem + extends AbstractNodeItem + implements IDMNodeItem { + @Nullable + private IResourceLocation resourceLocation; // null + + /** + * Construct a new node item. + */ + protected AbstractDMNodeItem() { + // only allow extending classes to create instances + } + + @Override + public IResourceLocation getLocation() { + return resourceLocation; + } + + @Override + public void setLocation(IResourceLocation location) { + this.resourceLocation = location; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AssemblyImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildAssemblyNodeItem.java similarity index 64% rename from core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AssemblyImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildAssemblyNodeItem.java index 7c4e047c7..a3725c7d3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/AssemblyImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildAssemblyNodeItem.java @@ -6,34 +6,36 @@ package gov.nist.secauto.metaschema.core.mdm.impl; import gov.nist.secauto.metaschema.core.mdm.IDMAssemblyNodeItem; -import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import edu.umd.cs.findbugs.annotations.NonNull; -public class AssemblyImpl - extends AbstractDMAssemblyNodeItem { +/** + * A Metapath assembly node item that is the child of another assembly node + * item. + */ +public class ChildAssemblyNodeItem + extends AbstractDMAssemblyNodeItem + implements IFeatureChildNodeItem { @NonNull private final IAssemblyInstance instance; @NonNull private final IDMAssemblyNodeItem parent; - @NonNull - private final IResourceLocation resourceLocation; - public AssemblyImpl( + /** + * Construct a new node item. + * + * @param instance + * the Metaschema module instance associated with this node + * @param parent + * the parent node item containing this node item + */ + public ChildAssemblyNodeItem( @NonNull IAssemblyInstance instance, - @NonNull IDMAssemblyNodeItem parent, - @NonNull IResourceLocation resourceLocation) { + @NonNull IDMAssemblyNodeItem parent) { this.instance = instance; this.parent = parent; - this.resourceLocation = resourceLocation; - } - - @Override - public IResourceLocation getLocation() { - return resourceLocation; } @Override @@ -62,9 +64,4 @@ public IAssemblyDefinition getDefinition() { public IAssemblyInstance getInstance() { return instance; } - - @Override - public StaticContext getStaticContext() { - return getParentNodeItem().getStaticContext(); - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFieldNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFieldNodeItem.java new file mode 100644 index 000000000..2e585321a --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFieldNodeItem.java @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; +import gov.nist.secauto.metaschema.core.model.IFieldDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldInstance; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A Metapath assembly node item that is the child of an assembly node item. + */ +public class ChildFieldNodeItem + extends AbstractDMFieldNodeItem + implements IDMFieldNodeItem, IFeatureChildNodeItem { + @NonNull + private final IFieldInstance instance; + @NonNull + private final IAssemblyNodeItem parent; + + /** + * Construct a new node item. + * + * @param instance + * the Metaschema module instance associated with this node + * @param parent + * the parent node item containing this node item + * @param value + * the initial field value + */ + public ChildFieldNodeItem( + @NonNull IFieldInstance instance, + @NonNull IAssemblyNodeItem parent, + @NonNull IAnyAtomicItem value) { + super(value); + this.instance = instance; + this.parent = parent; + } + + @Override + public Object getValue() { + return this; + } + + @Override + public int getPosition() { + return getParentNodeItem().getModelItemsByName(getQName()).indexOf(this); + } + + @Override + public IAssemblyNodeItem getParentNodeItem() { + return parent; + } + + @Override + public IAssemblyNodeItem getParentContentNodeItem() { + return getParentNodeItem(); + } + + @Override + public IFieldDefinition getDefinition() { + return instance.getDefinition(); + } + + @Override + public IFieldInstance getInstance() { + return instance; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFlagNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFlagNodeItem.java new file mode 100644 index 000000000..96f4e88eb --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/ChildFlagNodeItem.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.model.IFlagDefinition; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A Metapath assembly node item that is the child of an assembly or field node + * item. + */ +public class ChildFlagNodeItem + extends AbstractDMFlagNodeItem + implements IFeatureChildNodeItem> { + @NonNull + private final IFlagInstance instance; + @NonNull + private final IDMModelNodeItem parent; + + /** + * Construct a new node item. + * + * @param instance + * the Metaschema module instance associated with this node + * @param parent + * the parent node item containing this node item + * @param value + * the initial field value + */ + public ChildFlagNodeItem( + @NonNull IFlagInstance instance, + @NonNull IDMModelNodeItem parent, + @NonNull IAnyAtomicItem value) { + super(value); + this.instance = instance; + this.parent = parent; + } + + @Override + public IDMModelNodeItem getParentNodeItem() { + return parent; + } + + @Override + public Object getValue() { + return this; + } + + @Override + public IFlagDefinition getDefinition() { + return getInstance().getDefinition(); + } + + @Override + public IFlagInstance getInstance() { + return instance; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionAssemblyNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionAssemblyNodeItem.java new file mode 100644 index 000000000..365bf92f0 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionAssemblyNodeItem.java @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A Metapath assembly node item that is orphaned from a document-based data + * model. + */ +public class DefinitionAssemblyNodeItem + extends AbstractDMAssemblyNodeItem { + @NonNull + private final IAssemblyDefinition definition; + @NonNull + private final StaticContext staticContext; + + /** + * Construct a new node item. + * + * @param definition + * the Metaschema module definition associated with this node + * @param staticContext + * the static context to use when evaluating Metapath expressions + * against this node + */ + public DefinitionAssemblyNodeItem( + @NonNull IAssemblyDefinition definition, + @NonNull StaticContext staticContext) { + this.definition = definition; + this.staticContext = staticContext; + } + + @Override + public int getPosition() { + return 1; + } + + @Override + public INodeItem getParentNodeItem() { + // always null + return null; + } + + @Override + public IAssemblyNodeItem getParentContentNodeItem() { + // always null + return null; + } + + @Override + public IAssemblyDefinition getDefinition() { + return definition; + } + + @Override + public IAssemblyInstance getInstance() { + // always null + return null; + } + + @Override + public StaticContext getStaticContext() { + return staticContext; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionFieldNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionFieldNodeItem.java new file mode 100644 index 000000000..94334e89e --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DefinitionFieldNodeItem.java @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IAssemblyNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.model.IFieldDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldInstance; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A Metapath field node item that is orphaned from a document-based data model. + */ +public class DefinitionFieldNodeItem + extends AbstractDMFieldNodeItem { + @NonNull + private final IFieldDefinition definition; + @NonNull + private final StaticContext staticContext; + + /** + * Construct a new node item. + * + * @param definition + * the Metaschema module definition associated with this node + * @param value + * the initial field value + * @param staticContext + * the static context to use when evaluating Metapath expressions + * against this node + */ + public DefinitionFieldNodeItem( + @NonNull IFieldDefinition definition, + @NonNull IAnyAtomicItem value, + @NonNull StaticContext staticContext) { + super(value); + this.definition = definition; + this.staticContext = staticContext; + } + + @Override + public int getPosition() { + return 1; + } + + @Override + public INodeItem getParentNodeItem() { + // always null + return null; + } + + @Override + public IAssemblyNodeItem getParentContentNodeItem() { + // always null + return null; + } + + @Override + public IFieldDefinition getDefinition() { + return definition; + } + + @Override + public IFieldInstance getInstance() { + // always null + return null; + } + + @Override + public StaticContext getStaticContext() { + return staticContext; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentNodeItem.java similarity index 70% rename from core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentNodeItem.java index 822233e9a..2fdfb4f1a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/DocumentNodeItem.java @@ -12,7 +12,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; import gov.nist.secauto.metaschema.core.util.CollectionUtil; @@ -24,21 +23,30 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class DocumentImpl implements IDMDocumentNodeItem { +/** + * A Metapath document node item that is the top of a document-based data model. + */ +public class DocumentNodeItem + extends AbstractDMNodeItem + implements IDMDocumentNodeItem { @NonNull private final RootAssembly root; @NonNull - private final IResourceLocation resourceLocation; - @NonNull private final ISource source; - public DocumentImpl( + /** + * Construct a new node item. + * + * @param resource + * the Metaschema module instance resource this document is from + * @param root + * the root Metaschema module assembly definition that represents the + * root node of this document + */ + public DocumentNodeItem( @NonNull URI resource, - @NonNull IResourceLocation resourceLocation, - @NonNull IAssemblyDefinition root, - @NonNull IResourceLocation assemblyLocation) { - this.root = new RootAssembly(root, assemblyLocation); - this.resourceLocation = resourceLocation; + @NonNull IAssemblyDefinition root) { + this.root = new RootAssembly(root); this.source = ISource.externalSource(resource); } @@ -71,11 +79,6 @@ public IFlagNodeItem getFlagByName(IEnhancedQName name) { : CollectionUtil.emptyList(); } - @Override - public IResourceLocation getLocation() { - return resourceLocation; - } - @Override public String stringValue() { return ""; @@ -92,13 +95,8 @@ public Object getValue() { } @Override - public String toSignature() { - return ObjectUtils.notNull(new StringBuilder() - .append(getType().toSignature()) - .append('\u2ABB') - .append(getMetapath()) - .append('\u2ABC') - .toString()); + protected String getValueSignature() { + return null; } @Override @@ -111,29 +109,20 @@ private class RootAssembly implements IDMRootAssemblyNodeItem { @NonNull private final IAssemblyDefinition definition; - @NonNull - private final IResourceLocation resourceLocation; public RootAssembly( - @NonNull IAssemblyDefinition definition, - @NonNull IResourceLocation location) { + @NonNull IAssemblyDefinition definition) { this.definition = definition; - this.resourceLocation = location; } @Override public IEnhancedQName getQName() { - return definition.getRootQName(); - } - - @Override - public IResourceLocation getLocation() { - return resourceLocation; + return ObjectUtils.requireNonNull(definition.getRootQName(), "the definition is expected to have a root QName."); } @Override public IDocumentNodeItem getDocumentNodeItem() { - return DocumentImpl.this; + return DocumentNodeItem.this; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FlagImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FlagImpl.java deleted file mode 100644 index 1335777a3..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/FlagImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.mdm.impl; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; -import gov.nist.secauto.metaschema.core.model.IFlagDefinition; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class FlagImpl - extends AbstractDMInstanceNodeItem> - implements IFlagNodeItem { - @NonNull - private IAnyAtomicItem value; - - public FlagImpl( - @NonNull IFlagInstance instance, - @NonNull IModelNodeItem parent, - @NonNull IResourceLocation resourceLocation, - @NonNull IAnyAtomicItem value) { - super(instance, parent, resourceLocation); - this.value = value; - } - - @Override - public IAnyAtomicItem toAtomicItem() { - return value; - } - - @Override - public Object getValue() { - return this; - } - - @Override - public String stringValue() { - return toAtomicItem().asString(); - } - - @Override - protected String getValueSignature() { - return toAtomicItem().toSignature(); - } -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMAtomicValuedNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMAtomicValuedNodeItem.java deleted file mode 100644 index 6fa3c2af8..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMAtomicValuedNodeItem.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.mdm.impl; - -import gov.nist.secauto.metaschema.core.metapath.item.node.IAtomicValuedNodeItem; - -public interface IDMAtomicValuedNodeItem extends IAtomicValuedNodeItem { - @Override - default String stringValue() { - return toAtomicItem().asString(); - } -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMModelNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMModelNodeItem.java index f0df08fdc..96df47136 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMModelNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IDMModelNodeItem.java @@ -5,21 +5,46 @@ package gov.nist.secauto.metaschema.core.mdm.impl; +import gov.nist.secauto.metaschema.core.mdm.IDMFlagNodeItem; +import gov.nist.secauto.metaschema.core.mdm.IDMNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; import gov.nist.secauto.metaschema.core.model.IFlagInstance; import gov.nist.secauto.metaschema.core.model.IModelDefinition; import gov.nist.secauto.metaschema.core.model.INamedModelInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Represents a Metapath node item that is backed by a simple Metaschema + * module-based data model. + *

+ * Implementations of this interface are expected to support child flag node + * items. + *

+ * Child flags can be created using the + * {@link #newFlag(gov.nist.secauto.metaschema.core.model.IFlagInstance, IAnyAtomicItem)} + * method. These children are added to this assembly. + * + * @param + * the Java type of the definition associated with a Metaschema module + * @param + * the Java type of the instance associated with a Metaschema module + */ public interface IDMModelNodeItem - extends IModelNodeItem { + extends IModelNodeItem, IDMNodeItem, IDefinitionNodeItem { + /** + * Create and add a new flag to the underlying data model. + * + * @param instance + * the Metaschema flag instance describing the field + * @param value + * the atomic flag value + * @return the new flag node item + */ @NonNull - IFlagNodeItem newFlag( + IDMFlagNodeItem newFlag( @NonNull IFlagInstance instance, - @NonNull IResourceLocation resourceLocation, @NonNull IAnyAtomicItem value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IFeatureChildNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IFeatureChildNodeItem.java new file mode 100644 index 000000000..61417aed7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/mdm/impl/IFeatureChildNodeItem.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.mdm.impl; + +import gov.nist.secauto.metaschema.core.mdm.IDMNodeItem; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; +import gov.nist.secauto.metaschema.core.model.IModelDefinition; +import gov.nist.secauto.metaschema.core.model.INamedInstance; + +/** + * This feature identifies the implementing class as a node item that has a node + * item parent, providing default methods required by all child node items. + * + * @param

+ * the Java type of the parent node item + */ +public interface IFeatureChildNodeItem

> + extends IDMNodeItem { + + @Override + P getParentNodeItem(); + + @Override + default P getParentContentNodeItem() { + return getParentNodeItem(); + } + + @Override + default StaticContext getStaticContext() { + return getParentNodeItem().getStaticContext(); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java index 9a2a15799..14329df84 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IMetapathExpression.java @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.core.metapath.MetapathExpression.ConversionFunction; import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.library.FnBoolean; +import gov.nist.secauto.metaschema.core.metapath.impl.LazyCompilationMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; @@ -21,6 +22,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Supports compiling and executing Metapath expressions. + */ public interface IMetapathExpression { /** @@ -136,6 +140,23 @@ static IMetapathExpression compile(@NonNull String path, @NonNull StaticContext return MetapathExpression.compile(path, staticContext); } + /** + * Gets a new Metapath expression that is compiled on use. + *

+ * Lazy compilation may cause additional {@link MetapathException} errors at + * evaluation time, since compilation errors are not raised until evaluation. + * + * @param path + * the metapath expression + * @param staticContext + * the static evaluation context + * @return the expression object + */ + @NonNull + static IMetapathExpression lazyCompile(@NonNull String path, @NonNull StaticContext staticContext) { + return new LazyCompilationMetapathExpression(path, staticContext); + } + /** * Get the original Metapath expression as a string. * diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java index 94dcb57c4..4b9300644 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java @@ -22,6 +22,6 @@ public class FailingErrorListener @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException ex) { - throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg); + throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg, ex); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java index ba5d5ee47..113d9c1ac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/KindNodeTest.java @@ -28,10 +28,10 @@ public class KindNodeTest private final IItemType type; /** - * Construct a new expanded QName-based literal expression. + * Construct a new kind test expression. * - * @param name - * the literal value + * @param type + * the expected item type to test against */ public KindNodeTest(@NonNull IItemType type) { this.type = type; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java index 33d06978a..0c942077a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/WildcardNodeTest.java @@ -24,6 +24,7 @@ * wildcard name * test. */ +@SuppressWarnings("PMD.TestClassWithoutTestCases") public class WildcardNodeTest implements INodeTestExpression { @Nullable private final Predicate> matcher; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMore.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMore.java index 0399ffa31..d44207078 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMore.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOneOrMore.java @@ -72,7 +72,7 @@ private static ISequence execute(@NonNull IFunction function, */ @NonNull public static ISequence fnOneOrMore(@NonNull ISequence sequence) { - if (sequence.size() < 1) { + if (sequence.isEmpty()) { throw new InvalidArgumentFunctionException( InvalidArgumentFunctionException.INVALID_ARGUMENT_ONE_OR_MORE, String.format("fn:one-or-more called with the sequence '%s' containing less than one item.", diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/IFeatureCollectionFunctionItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/IFeatureCollectionFunctionItem.java index ebcc5ae02..e4563276a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/IFeatureCollectionFunctionItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/IFeatureCollectionFunctionItem.java @@ -16,6 +16,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Provides default methods for node items that represent collections of + * Metapath items and sequences. + */ public interface IFeatureCollectionFunctionItem extends IFunction { /** * The function properties. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java new file mode 100644 index 000000000..eca627b1f --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/LazyCompilationMetapathExpression.java @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.impl; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.MetapathException; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.ISequence; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import edu.umd.cs.findbugs.annotations.NonNull; +import nl.talsmasoftware.lazy4j.Lazy; + +/** + * An implementation of a Metapath expression that is compiled when evaluated. + *

+ * Lazy compilation may cause additional {@link MetapathException} errors at + * evaluation time, since compilation errors are not raised until evaluation. + */ +public class LazyCompilationMetapathExpression implements IMetapathExpression { + @NonNull + private final String path; + @NonNull + private final StaticContext staticContext; + @NonNull + private final Lazy compiledMetapath; + + /** + * Construct a new lazy-compiled Metapath expression. + * + * @param path + * the metapath expression + * @param staticContext + * the static evaluation context + */ + public LazyCompilationMetapathExpression( + @NonNull String path, + @NonNull StaticContext staticContext) { + this.path = path; + this.staticContext = staticContext; + this.compiledMetapath = ObjectUtils.notNull(Lazy.lazy(() -> IMetapathExpression.compile(path, staticContext))); + } + + @Override + public String getPath() { + return path; + } + + @Override + public StaticContext getStaticContext() { + return staticContext; + } + + @NonNull + private IMetapathExpression getCompiledMetapath() { + return ObjectUtils.notNull(compiledMetapath.get()); + } + + @Override + public ISequence evaluate(IItem focus, DynamicContext dynamicContext) { + return getCompiledMetapath().evaluate(focus, dynamicContext); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractGlobalDefinitionNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractGlobalDefinitionNodeItem.java index 2c0a62470..16e50f847 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractGlobalDefinitionNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/AbstractGlobalDefinitionNodeItem.java @@ -13,7 +13,9 @@ * Represents a node item based on a global definition from a Metaschema. * * @param - * the definition type + * the Java type of the associated Metaschema module definition + * @param + * the Java type of the associated Metaschema module instance */ public abstract class AbstractGlobalDefinitionNodeItem extends AbstractDefinitionNodeItem { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDefinitionNodeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDefinitionNodeItem.java index 4dec59e28..b531267c8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDefinitionNodeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/IDefinitionNodeItem.java @@ -9,6 +9,15 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Represents a Metapath node item that is based on an underlying Metaschema + * module definition and instance. + * + * @param + * the Java type of the definition associated with a Metaschema module + * @param + * the Java type of the instance associated with a Metaschema module + */ public interface IDefinitionNodeItem extends INodeItem { /** * Get the name of this node. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/package-info.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/package-info.java index cda49f921..4d1871000 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/package-info.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/node/package-info.java @@ -4,8 +4,8 @@ * represented using a Metaschema model. *

* The factory - * {@link gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory} - * can be used to create instances of various node item objects. + * {@link gov.nist.secauto.metaschema.databind.model.impl.INodeItemFactory} can + * be used to create instances of various node item objects. */ package gov.nist.secauto.metaschema.core.metapath.item.node; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/ISource.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/ISource.java index 2c1c8a76e..8efee288c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/ISource.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/ISource.java @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.constraint.impl.InternalModelSource; import gov.nist.secauto.metaschema.core.model.constraint.impl.StaticContextSource; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.net.URI; @@ -47,6 +48,21 @@ static ISource moduleSource(@NonNull IModule module) { return InternalModelSource.instance(module); } + /** + * Get the descriptor for a + * {@link gov.nist.secauto.metaschema.core.model.ISource.SourceLocation#EXTERNAL} + * source for the provided resource. + * + * @param location + * the resource used as the source + * + * @return the source descriptor + */ + @NonNull + static ISource externalSource(@NonNull String location) { + return externalSource(ObjectUtils.notNull(URI.create(location))); + } + /** * Get the descriptor for a * {@link gov.nist.secauto.metaschema.core.model.ISource.SourceLocation#EXTERNAL} @@ -62,7 +78,8 @@ static ISource externalSource(@NonNull URI location) { return StaticContextSource.instance( StaticContext.builder() .baseUri(location) - .build()); + .build(), + true); } /** @@ -76,15 +93,20 @@ static ISource externalSource(@NonNull URI location) { * @param staticContext * the static Metapath context to use for compiling Metapath * expressions in this source + * @param useCached + * if {@code true} use a previously cached source, otherwise create a + * new one * * @return the source descriptor */ @NonNull - static ISource externalSource(@NonNull StaticContext staticContext) { + static ISource externalSource( + @NonNull StaticContext staticContext, + boolean useCached) { if (staticContext.getBaseUri() == null) { throw new IllegalArgumentException("The static content must define a baseUri identifing the source resource."); } - return StaticContextSource.instance(staticContext); + return StaticContextSource.instance(staticContext, useCached); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java index 0d1867d81..452bceace 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConfigurableMessageConstraintBuilder.java @@ -24,6 +24,7 @@ public abstract class AbstractConfigurableMessageConstraintBuilder< T extends AbstractConfigurableMessageConstraintBuilder, R extends IConfigurableMessageConstraint> extends AbstractConstraintBuilder { + @Nullable private String message; /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java index 1ab6d3fa9..8161dbf0c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintBuilder.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level; @@ -42,7 +43,7 @@ public abstract class AbstractConstraintBuilder< @NonNull private Level level = IConstraint.DEFAULT_LEVEL; @NonNull - private String target = IConstraint.DEFAULT_TARGET_METAPATH; + private IMetapathExpression target = IConstraint.DEFAULT_TARGET_METAPATH; @NonNull private Map> properties = new LinkedHashMap<>(); // NOPMD not thread safe private MarkupMultiline remarks; @@ -131,7 +132,7 @@ public T level(@NonNull Level level) { * @return this builder */ @NonNull - public T target(@NonNull String target) { + public T target(@NonNull IMetapathExpression target) { this.target = target; return getThis(); } @@ -283,7 +284,7 @@ protected Level getLevel() { * @return the target Metapath expression */ @NonNull - protected String getTarget() { + protected IMetapathExpression getTarget() { return target; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java index 678c22cab..f5a1f2a20 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractConstraintValidationHandler.java @@ -87,7 +87,7 @@ protected String newCardinalityMinimumViolationMessage( "The cardinality '%d' is below the required minimum '%d' for items matching '%s'.", testedItems.size(), constraint.getMinOccurs(), - constraint.getTarget())) + constraint.getTarget().getPath())) : constraint.generateMessage(target, dynamicContext); } @@ -284,7 +284,7 @@ protected String newExpectViolationMessage( @NonNull DynamicContext dynamicContext) { return constraint.getMessage() == null ? ObjectUtils.notNull(String.format("Expect constraint '%s' did not match the data at path '%s'", - constraint.getTest(), + constraint.getTest().getPath(), toPath(target))) : constraint.generateMessage(target, dynamicContext); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractTargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractTargetedConstraints.java index d010cf162..b2666ef28 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractTargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AbstractTargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.ISource; import edu.umd.cs.findbugs.annotations.NonNull; @@ -21,7 +22,7 @@ public abstract class AbstractTargetedConstraints @NonNull private final ISource source; @NonNull - private final String targetExpression; + private final IMetapathExpression target; @NonNull private final T constraints; @@ -37,10 +38,10 @@ public abstract class AbstractTargetedConstraints */ protected AbstractTargetedConstraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull T constraints) { this.source = source; - this.targetExpression = target; + this.target = target; this.constraints = constraints; } @@ -50,8 +51,8 @@ public ISource getSource() { } @Override - public String getTargetExpression() { - return targetExpression; + public IMetapathExpression getTarget() { + return target; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AssemblyTargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AssemblyTargetedConstraints.java index e50f5b592..233273f6c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AssemblyTargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/AssemblyTargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.impl.AbstractDefinitionTargetedConstraints; @@ -15,7 +16,7 @@ * A set of constraints targeting a {@link IAssemblyDefinition} based on a * target Metapath expression. * - * @see #getTargetExpression() + * @see #getTarget() */ public class AssemblyTargetedConstraints extends AbstractDefinitionTargetedConstraints @@ -33,7 +34,7 @@ public class AssemblyTargetedConstraints */ public AssemblyTargetedConstraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull IModelConstrained constraints) { super(source, target, constraints); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java index 0fadac1dc..3f6f414c4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/DefaultConstraintValidator.java @@ -392,7 +392,7 @@ private static String toErrorMessage( String id = constraint.getId(); if (id == null) { builder.append(" targeting the metapath '") - .append(constraint.getTarget()) + .append(constraint.getTarget().getPath()) .append('\''); } else { builder.append(" with id '") @@ -677,10 +677,7 @@ private void validateExpect( @NonNull ISequence targets, @NonNull DynamicContext dynamicContext) { try { - IMetapathExpression metapath = IMetapathExpression.compile( - constraint.getTest(), - dynamicContext.getStaticContext()); - + IMetapathExpression metapath = constraint.getTest(); IConstraintValidationHandler handler = getConstraintValidationHandler(); targets.stream() .forEachOrdered(item -> { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessor.java index acd3a89ff..26c27eaf5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessor.java @@ -92,8 +92,7 @@ private static void applyConstraints( @NonNull DynamicContext dynamicContext) { for (ITargetedConstraints targeted : set.getTargetedConstraintsForModule(module)) { // apply targeted constraints - String targetExpression = targeted.getTargetExpression(); - IMetapathExpression metapath = IMetapathExpression.compile(targetExpression, dynamicContext.getStaticContext()); + IMetapathExpression metapath = targeted.getTarget(); ISequence items = metapath.evaluate(moduleItem, dynamicContext); assert items != null; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FieldTargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FieldTargetedConstraints.java index 4078630ab..24e0c0e4d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FieldTargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FieldTargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.impl.AbstractDefinitionTargetedConstraints; @@ -15,7 +16,7 @@ * A set of constraints targeting a {@link IFieldDefinition} based on a target * Metapath expression. * - * @see #getTargetExpression() + * @see #getTarget() */ public class FieldTargetedConstraints extends AbstractDefinitionTargetedConstraints { @@ -32,7 +33,7 @@ public class FieldTargetedConstraints */ public FieldTargetedConstraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull IValueConstrained constraints) { super(source, target, constraints); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FlagTargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FlagTargetedConstraints.java index 1981ed572..7bf15a9ac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FlagTargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/FlagTargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IFlagDefinition; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.impl.AbstractDefinitionTargetedConstraints; @@ -15,7 +16,7 @@ * A set of constraints targeting a {@link IFlagDefinition} based on a target * Metapath expression. * - * @see #getTargetExpression() + * @see #getTarget() */ public class FlagTargetedConstraints extends AbstractDefinitionTargetedConstraints { @@ -32,7 +33,7 @@ public class FlagTargetedConstraints */ public FlagTargetedConstraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull IValueConstrained constraints) { super(source, target, constraints); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java index b70d13e57..473817e5c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.item.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; @@ -90,10 +91,10 @@ enum Level { Level DEFAULT_LEVEL = Level.ERROR; /** - * The default target Metapath expression to use if no target is provided. + * The compiled default target Metapath to use if no target is provided. */ @NonNull - String DEFAULT_TARGET_METAPATH = "."; + IMetapathExpression DEFAULT_TARGET_METAPATH = IMetapathExpression.contextNode(); /** * Get a string that identifies the provided constraint using the most specific @@ -111,7 +112,7 @@ static String getConstraintIdentity(@NonNull IConstraint constraint) { } else if (constraint.getFormalName() != null) { identity = String.format("with the formal name '%s'", constraint.getFormalName()); } else { - identity = String.format("targeting '%s'", constraint.getTarget()); + identity = String.format("targeting '%s'", constraint.getTarget().getPath()); } return ObjectUtils.notNull(identity); } @@ -155,7 +156,7 @@ static String getConstraintIdentity(@NonNull IConstraint constraint) { * @return a Metapath expression */ @NonNull - String getTarget(); + IMetapathExpression getTarget(); /** * Based on the provided {@code contextNodeItem}, find all nodes matching the diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java index 16b9e563e..db770ef77 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IExpectConstraint.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.constraint.impl.DefaultExpectConstraint; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -28,7 +29,7 @@ default Type getType() { * @return the test metapath expression to use */ @NonNull - String getTest(); + IMetapathExpression getTest(); @Override default R accept(IConstraintVisitor visitor, T state) { @@ -50,7 +51,7 @@ static Builder builder() { */ final class Builder extends AbstractConfigurableMessageConstraintBuilder { - private String test; + private IMetapathExpression test; private Builder() { // disable construction @@ -64,7 +65,7 @@ private Builder() { * @return this builder */ @NonNull - public Builder test(@NonNull String test) { + public Builder test(@NonNull IMetapathExpression test) { this.test = test; return this; } @@ -81,7 +82,7 @@ protected void validate() { ObjectUtils.requireNonNull(getTest()); } - private String getTest() { + private IMetapathExpression getTest() { return test; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java index 06d4d5ace..45914b155 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IIndex.java @@ -160,7 +160,7 @@ private static String buildKeyItem( @NonNull INodeItem item, @NonNull IKeyField keyField, @NonNull DynamicContext dynamicContext) { - IMetapathExpression keyMetapath = keyField.getTargetMetapath(); + IMetapathExpression keyMetapath = keyField.getTarget(); IItem keyItem; try { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyField.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyField.java index 54e91447d..8c0396c5b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyField.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/IKeyField.java @@ -7,7 +7,6 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.impl.DefaultKeyField; import java.util.regex.Pattern; @@ -33,18 +32,15 @@ public interface IKeyField { * an optional used to extract a portion of the resulting key value * @param remarks * optional remarks describing the intent of the constraint - * @param source - * the descriptor for the resource containing the constraint * @return the new key field */ @SuppressWarnings("PMD.ShortMethodName") @NonNull static IKeyField of( - @NonNull String target, + @NonNull IMetapathExpression target, @Nullable Pattern pattern, - @Nullable MarkupMultiline remarks, - @NonNull ISource source) { - return new DefaultKeyField(target, pattern, remarks, source); + @Nullable MarkupMultiline remarks) { + return new DefaultKeyField(target, pattern, remarks); } /** @@ -54,15 +50,7 @@ static IKeyField of( * @return the Metapath expression identifying the key value target */ @NonNull - String getTarget(); - - /** - * Get the compiled Metapath expression for the {@link #getTarget()}. - * - * @return the compiled Metapath expression - */ - @NonNull - IMetapathExpression getTargetMetapath(); + IMetapathExpression getTarget(); /** * A pattern to use to retrieve the value. If non-{@code null}, the first diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ITargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ITargetedConstraints.java index e2ca5c5bb..81ab656d1 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ITargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/ITargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; import gov.nist.secauto.metaschema.core.model.IFlagDefinition; @@ -31,7 +32,7 @@ public interface ITargetedConstraints extends IValueConstrained { * @return the uncompiled Metapath expression */ @NonNull - String getTargetExpression(); + IMetapathExpression getTarget(); /** * Apply the constraint to the provided definition. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java index 68357c50f..8eb801a02 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraint.java @@ -71,7 +71,7 @@ protected AbstractConfigurableMessageConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @Nullable String message, @Nullable MarkupMultiline remarks) { @@ -97,8 +97,17 @@ public String generateMessage(@NonNull INodeItem item, @NonNull DynamicContext c return ObjectUtils.notNull(ReplacementScanner.replaceTokens(message, METAPATH_VALUE_TEMPLATE_PATTERN, match -> { String metapath = ObjectUtils.notNull(match.group(2)); try { - IMetapathExpression expr = IMetapathExpression.compile(metapath, context.getStaticContext()); - return expr.evaluateAs(item, IMetapathExpression.ResultType.STRING, context); + IMetapathExpression expr = IMetapathExpression.compile( + metapath, + // need to use the static context of the source to resolve prefixes, etc., since + // this is where the message is defined + getSource().getStaticContext()); + return expr.evaluateAs( + item, + IMetapathExpression.ResultType.STRING, + // here we are using the static context of the instance, since this is how + // variables and nodes are resolved. + context); } catch (MetapathException ex) { throw new MetapathException( String.format("Unable to evaluate the message replacement expression '%s' in constraint '%s'. %s", diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java index 66f0f8599..108a9b113 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConstraint.java @@ -23,7 +23,6 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import nl.talsmasoftware.lazy4j.Lazy; /** * The base class for all constraint implementations. @@ -44,7 +43,7 @@ public abstract class AbstractConstraint implements IConstraint { // NOPMD - int @NonNull private final Map> properties; @NonNull - private final Lazy targetMetapath; + private final IMetapathExpression target; /** * Construct a new Metaschema constraint. @@ -73,7 +72,7 @@ protected AbstractConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @Nullable MarkupMultiline remarks) { Objects.requireNonNull(target); @@ -84,10 +83,7 @@ protected AbstractConstraint( this.level = ObjectUtils.requireNonNull(level, "level"); this.properties = properties; this.remarks = remarks; - this.targetMetapath = ObjectUtils.notNull( - Lazy.lazy(() -> IMetapathExpression.compile( - target, - source.getStaticContext()))); + this.target = target; } @Override @@ -117,8 +113,8 @@ public Level getLevel() { } @Override - public final String getTarget() { - return getTargetMetapath().getPath(); + public final IMetapathExpression getTarget() { + return target; } @Override @@ -131,21 +127,11 @@ public MarkupMultiline getRemarks() { return remarks; } - /** - * Get the compiled Metapath expression for the target. - * - * @return the compiled Metapath expression - */ - @NonNull - public final IMetapathExpression getTargetMetapath() { - return ObjectUtils.notNull(targetMetapath.get()); - } - @Override @NonNull public ISequence> matchTargets( @NonNull IDefinitionNodeItem item, @NonNull DynamicContext dynamicContext) { - return item.hasValue() ? getTargetMetapath().evaluate(item, dynamicContext) : ISequence.empty(); + return item.hasValue() ? getTarget().evaluate(item, dynamicContext) : ISequence.empty(); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractDefinitionTargetedConstraints.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractDefinitionTargetedConstraints.java index 853607051..d589bde2a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractDefinitionTargetedConstraints.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractDefinitionTargetedConstraints.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.constraint.impl; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IDefinition; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; @@ -45,7 +46,7 @@ public abstract class AbstractDefinitionTargetedConstraints< */ protected AbstractDefinitionTargetedConstraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull S constraints) { super(source, target, constraints); } @@ -89,7 +90,7 @@ private void wrongDefinitionTypeTargeted(@NonNull IDefinition definition) { definition.getModelType().name().toLowerCase(Locale.ROOT), definition.getEffectiveName(), definition.getContainingModule().getQName().toString(), - getTargetExpression(), + getTarget(), getSource().getLocationHint())); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java index c7fcb1647..daa6e1550 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractKeyConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; @@ -61,7 +62,7 @@ protected AbstractKeyConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @NonNull List keyFields, @Nullable String message, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/ConstraintComposingVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/ConstraintComposingVisitor.java index 5ade00e26..9a06f43d8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/ConstraintComposingVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/ConstraintComposingVisitor.java @@ -68,7 +68,7 @@ private static void illegalTargetError( "Invalid target '%s' for constraints targeting '%s' in '%s'. A document node is an" + " invalid constraint target. Constraints can only apply to an assembly, field, or flag definition.", item.getMetapath(), - context.getTargetExpression(), + context.getTarget(), context.getSource().getLocationHint())); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java index 3b5650a08..f84944154 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultAllowedValuesConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue; @@ -70,7 +71,7 @@ public DefaultAllowedValuesConstraint( // NOPMD necessary @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @NonNull Map allowedValues, boolean allowedOther, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java index 3db8a01a4..478db95c6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultCardinalityConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; @@ -70,7 +71,7 @@ public DefaultCardinalityConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @Nullable Integer minOccurs, @Nullable Integer maxOccurs, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java index a1c00f953..744bdd46f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultExpectConstraint.java @@ -12,14 +12,12 @@ import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Map; import java.util.Set; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import nl.talsmasoftware.lazy4j.Lazy; /** * Represents an expect constraint. @@ -31,7 +29,7 @@ public final class DefaultExpectConstraint extends AbstractConfigurableMessageConstraint implements IExpectConstraint { @NonNull - private final Lazy testMetapath; + private final IMetapathExpression test; /** * Construct a new expect constraint. @@ -66,30 +64,17 @@ public DefaultExpectConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, - @NonNull String test, + @NonNull IMetapathExpression test, @Nullable String message, @Nullable MarkupMultiline remarks) { super(id, formalName, description, source, level, target, properties, message, remarks); - this.testMetapath = ObjectUtils.notNull( - Lazy.lazy(() -> IMetapathExpression.compile( - test, - source.getStaticContext()))); - } - - /** - * Get the compiled Metapath expression for the test. - * - * @return the compiled Metapath expression - */ - @NonNull - public IMetapathExpression getTestMetapath() { - return ObjectUtils.notNull(testMetapath.get()); + this.test = test; } @Override - public String getTest() { - return getTestMetapath().getPath(); + public IMetapathExpression getTest() { + return test; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexConstraint.java index c5fb4a241..7a8e6ee74 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; @@ -69,7 +70,7 @@ public DefaultIndexConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @NonNull String name, @NonNull List keyFields, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexHasKeyConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexHasKeyConstraint.java index 3ac34f5d6..1303764b7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexHasKeyConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultIndexHasKeyConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; @@ -67,7 +68,7 @@ public DefaultIndexHasKeyConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @NonNull String indexName, @NonNull List keyFields, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultKeyField.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultKeyField.java index 9be2c9feb..3a8f35d1e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultKeyField.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultKeyField.java @@ -7,22 +7,19 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; -import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IKeyField; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.regex.Pattern; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import nl.talsmasoftware.lazy4j.Lazy; public class DefaultKeyField implements IKeyField { @Nullable private final Pattern pattern; @NonNull - private final Lazy target; + private final IMetapathExpression target; @Nullable private final MarkupMultiline remarks; @@ -36,16 +33,13 @@ public class DefaultKeyField implements IKeyField { * an optional used to extract a portion of the resulting key value * @param remarks * optional remarks describing the intent of the constraint - * @param source - * the descriptor for the resource containing the constraint */ public DefaultKeyField( - @NonNull String target, + @NonNull IMetapathExpression target, @Nullable Pattern pattern, - @Nullable MarkupMultiline remarks, - @NonNull ISource source) { + @Nullable MarkupMultiline remarks) { this.pattern = pattern; - this.target = ObjectUtils.notNull(Lazy.lazy(() -> IMetapathExpression.compile(target, source.getStaticContext()))); + this.target = target; this.remarks = remarks; } @@ -55,13 +49,8 @@ public Pattern getPattern() { } @Override - public String getTarget() { - return getTargetMetapath().getPath(); - } - - @Override - public IMetapathExpression getTargetMetapath() { - return ObjectUtils.notNull(target.get()); + public IMetapathExpression getTarget() { + return target; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java index 952769386..0f581c8e5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultMatchesConstraint.java @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.ConstraintInitializationException; @@ -68,7 +69,7 @@ public DefaultMatchesConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @Nullable Pattern pattern, @Nullable IDataTypeAdapter dataType, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultUniqueConstraint.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultUniqueConstraint.java index 4293feb1b..1bd9f3f23 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultUniqueConstraint.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/DefaultUniqueConstraint.java @@ -7,6 +7,7 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.IKeyField; @@ -60,7 +61,7 @@ public DefaultUniqueConstraint( @Nullable MarkupLine description, @NonNull ISource source, @NonNull Level level, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull Map> properties, @NonNull List keyFields, @Nullable String message, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/StaticContextSource.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/StaticContextSource.java index 0e0262039..601aa438b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/StaticContextSource.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/constraint/impl/StaticContextSource.java @@ -38,10 +38,22 @@ public final class StaticContextSource implements ISource { * @param staticContext * the static Metapath context to use for compiling Metapath * expressions in this source + * @param useCached + * if {@code true} use a previously cached source, otherwise create a + * new one * @return the source */ @NonNull - public static ISource instance(@NonNull StaticContext staticContext) { + public static ISource instance( + @NonNull StaticContext staticContext, + boolean useCached) { + return useCached + ? retrieveFromCache(staticContext) + : new StaticContextSource(staticContext); + } + + @NonNull + private static StaticContextSource retrieveFromCache(@NonNull StaticContext staticContext) { SOURCE_LOCK.lock(); try { return ObjectUtils.notNull(sources.computeIfAbsent( diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java index 35b0a3f37..47bcd0fb9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlConstraintLoader.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.xml; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.AbstractLoader; @@ -173,7 +174,7 @@ protected List parseScopedConstraints( builder.useWildcardWhenNamespaceNotDefaulted(true); - ISource source = ISource.externalSource(resource); + ISource source = ISource.externalSource(builder.build(), false); for (Scope scope : constraints.getScopeList()) { assert scope != null; @@ -214,7 +215,9 @@ private static void handleScopedAssembly( state.getRight().add(new AssemblyTargetedConstraints( source, - ObjectUtils.requireNonNull(assembly.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(assembly.getTarget()), + source.getStaticContext()), constraints)); } @@ -229,7 +232,9 @@ private static void handleScopedField( // NOPMD false positive state.getRight().add(new FieldTargetedConstraints( source, - ObjectUtils.requireNonNull(field.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(field.getTarget()), + source.getStaticContext()), constraints)); } @@ -244,7 +249,9 @@ private static void handleScopedFlag( // NOPMD false positive state.getRight().add(new FlagTargetedConstraints( source, - ObjectUtils.requireNonNull(flag.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(flag.getTarget()), + source.getStaticContext()), constraints)); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlMetaConstraintLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlMetaConstraintLoader.java index f12b8578b..f3e0b70d0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlMetaConstraintLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/XmlMetaConstraintLoader.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.core.model.xml; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.AbstractLoader; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; @@ -66,7 +67,7 @@ protected List parseResource(URI resource, Deque visitedRes ObjectUtils.notNull(binding.getPrefix()), ObjectUtils.notNull(binding.getUri()))); builder.useWildcardWhenNamespaceNotDefaulted(true); - ISource source = ISource.externalSource(resource); + ISource source = ISource.externalSource(builder.build(), true); List targetedConstraints = ObjectUtils.notNull(constraints.getContextList().stream() .flatMap(context -> parseContext(ObjectUtils.notNull(context), null, source).getTargetedConstraints().stream()) @@ -151,7 +152,12 @@ public Context( public List getTargetedConstraints() { return Stream.concat( getMetapaths().stream() - .map(metapath -> new MetaTargetedContraints(source, ObjectUtils.notNull(metapath), constraints)), + .map(metapath -> new MetaTargetedContraints( + source, + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(metapath), + source.getStaticContext()), + constraints)), childContexts.stream() .flatMap(child -> child.getTargetedConstraints().stream())) .collect(Collectors.toList()); @@ -172,7 +178,7 @@ private static class MetaTargetedContraints protected MetaTargetedContraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull IModelConstrained constraints) { super(source, target, constraints); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java index bb5aa223c..b03298c89 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ConstraintXmlSupport.java @@ -438,7 +438,7 @@ public Void visitAllowedValues(IAllowedValuesConstraint constraint, DefineAssemb if (Boolean.compare(IAllowedValuesConstraint.ALLOW_OTHER_DEFAULT, constraint.isAllowedOther()) != 0) { bean.setAllowOther(constraint.isAllowedOther()); } - bean.setTarget(constraint.getTarget()); + bean.setTarget(constraint.getTarget().getPath()); bean.setExtensible(constraint.getExtensible()); for (Map.Entry entry : constraint.getAllowedValues().entrySet()) { @@ -494,7 +494,7 @@ public Void visitExpectConstraint(IExpectConstraint constraint, DefineAssemblyCo assert bean != null; applyCommonValues(constraint, bean); - bean.setTest(constraint.getTest()); + bean.setTest(constraint.getTest().getPath()); String message = constraint.getMessage(); if (message != null) { @@ -550,7 +550,7 @@ private static void applyKeyField(@NonNull IKeyField keyField, @NonNull KeyField bean.setPattern(pattern); } - bean.setTarget(keyField.getTarget()); + bean.setTarget(keyField.getTarget().getPath()); MarkupMultiline remarks = keyField.getRemarks(); if (remarks != null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java index e13724971..d28bb4869 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/ModelFactory.java @@ -6,6 +6,7 @@ package gov.nist.secauto.metaschema.core.model.xml.impl; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder; @@ -63,8 +64,10 @@ private ModelFactory() { } @NonNull - private static String target(@Nullable String target) { - return target == null ? IConstraint.DEFAULT_TARGET_METAPATH : target; + private static IMetapathExpression metapath(@Nullable String target, @NonNull ISource source) { + return target == null + ? IConstraint.DEFAULT_TARGET_METAPATH + : IMetapathExpression.lazyCompile(target, source.getStaticContext()); } @NonNull @@ -145,7 +148,7 @@ private static Map toAllowedValues( public static IAllowedValuesConstraint newAllowedValuesConstraint( @NonNull TargetedAllowedValuesConstraintType xmlObject, @NonNull ISource source) { - return newAllowedValuesConstraint(xmlObject, target(xmlObject.getTarget()), source); + return newAllowedValuesConstraint(xmlObject, metapath(xmlObject.getTarget(), source), source); } /** @@ -167,7 +170,7 @@ public static IAllowedValuesConstraint newAllowedValuesConstraint( @NonNull private static IAllowedValuesConstraint newAllowedValuesConstraint( @NonNull AllowedValuesType xmlObject, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull ISource source) { IAllowedValuesConstraint.Builder builder = IAllowedValuesConstraint.builder(); @@ -192,7 +195,7 @@ private static IAllowedValuesConstraint newAllowedValuesConstraint( @NonNull private static > T applyToBuilder( @NonNull ConstraintType xmlObject, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull ISource source, @NonNull T builder) { @@ -218,7 +221,7 @@ private static IAllowedValuesConstraint newAllowedValuesConstraint( public static IMatchesConstraint newMatchesConstraint( @NonNull TargetedMatchesConstraintType xmlObject, @NonNull ISource source) { - return newMatchesConstraint(xmlObject, target(xmlObject.getTarget()), source); + return newMatchesConstraint(xmlObject, metapath(xmlObject.getTarget(), source), source); } /** @@ -240,7 +243,7 @@ public static IMatchesConstraint newMatchesConstraint( @NonNull private static IMatchesConstraint newMatchesConstraint( @NonNull MatchesConstraintType xmlObject, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull ISource source) { IMatchesConstraint.Builder builder = IMatchesConstraint.builder(); @@ -270,10 +273,9 @@ private static void buildKeyFields( @NonNull ISource source) { for (KeyConstraintType.KeyField xmlKeyField : xmlObject.getKeyFieldList()) { IKeyField keyField = IKeyField.of( - ObjectUtils.requireNonNull(xmlKeyField.getTarget()), + metapath(ObjectUtils.requireNonNull(xmlKeyField.getTarget()), source), xmlKeyField.isSetPattern() ? xmlKeyField.getPattern() : null, // NOPMD - intentional - xmlKeyField.isSetRemarks() ? remarks(ObjectUtils.notNull(xmlKeyField.getRemarks())) : null, - source); + xmlKeyField.isSetRemarks() ? remarks(ObjectUtils.notNull(xmlKeyField.getRemarks())) : null); builder.keyField(keyField); } } @@ -293,7 +295,7 @@ public static IUniqueConstraint newUniqueConstraint( @NonNull ISource source) { IUniqueConstraint.Builder builder = IUniqueConstraint.builder(); - applyToBuilder(xmlObject, target(xmlObject.getTarget()), source, builder); + applyToBuilder(xmlObject, metapath(xmlObject.getTarget(), source), source, builder); if (xmlObject.isSetMessage()) { builder.message(ObjectUtils.notNull(xmlObject.getMessage())); @@ -323,7 +325,7 @@ public static IIndexConstraint newIndexConstraint( @NonNull ISource source) { IIndexConstraint.Builder builder = IIndexConstraint.builder(ObjectUtils.requireNonNull(xmlObject.getName())); - applyToBuilder(xmlObject, target(xmlObject.getTarget()), source, builder); + applyToBuilder(xmlObject, metapath(xmlObject.getTarget(), source), source, builder); if (xmlObject.isSetMessage()) { builder.message(ObjectUtils.notNull(xmlObject.getMessage())); @@ -351,7 +353,7 @@ public static IIndexConstraint newIndexConstraint( public static IIndexHasKeyConstraint newIndexHasKeyConstraint( @NonNull TargetedIndexHasKeyConstraintType xmlObject, @NonNull ISource source) { - return newIndexHasKeyConstraint(xmlObject, target(xmlObject.getTarget()), source); + return newIndexHasKeyConstraint(xmlObject, metapath(xmlObject.getTarget(), source), source); } /** @@ -373,7 +375,7 @@ public static IIndexHasKeyConstraint newIndexHasKeyConstraint( @NonNull private static IIndexHasKeyConstraint newIndexHasKeyConstraint( @NonNull IndexHasKeyConstraintType xmlObject, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull ISource source) { IIndexHasKeyConstraint.Builder builder = IIndexHasKeyConstraint.builder(ObjectUtils.requireNonNull(xmlObject.getName())); @@ -406,7 +408,7 @@ private static IIndexHasKeyConstraint newIndexHasKeyConstraint( public static IExpectConstraint newExpectConstraint( @NonNull TargetedExpectConstraintType xmlObject, @NonNull ISource source) { - return newExpectConstraint(xmlObject, target(xmlObject.getTarget()), source); + return newExpectConstraint(xmlObject, metapath(xmlObject.getTarget(), source), source); } /** @@ -428,7 +430,7 @@ public static IExpectConstraint newExpectConstraint( @NonNull private static IExpectConstraint newExpectConstraint( @NonNull ExpectConstraintType xmlObject, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull ISource source) { IExpectConstraint.Builder builder = IExpectConstraint.builder(); @@ -443,7 +445,10 @@ private static IExpectConstraint newExpectConstraint( builder.remarks(remarks(ObjectUtils.notNull(xmlObject.getRemarks()))); } - builder.test(ObjectUtils.requireNonNull(xmlObject.getTest())); + builder.test( + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(xmlObject.getTest()), + source.getStaticContext())); return builder.build(); } @@ -464,7 +469,7 @@ public static ICardinalityConstraint newCardinalityConstraint( ICardinalityConstraint.Builder builder = ICardinalityConstraint.builder(); - applyToBuilder(xmlObject, target(xmlObject.getTarget()), source, builder); + applyToBuilder(xmlObject, metapath(xmlObject.getTarget(), source), source, builder); if (xmlObject.isSetMessage()) { builder.message(ObjectUtils.notNull(xmlObject.getMessage())); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java index ad89574e0..76e94165c 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnHasChildrenTest.java @@ -62,7 +62,7 @@ private static Stream provideValues() { // NOPMD - false positive void test(boolean expected, @NonNull String metapath) { DynamicContext dynamicContext = newDynamicContext(); - INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(getContext()); + INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(); Boolean result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) .evaluateAs(node, IMetapathExpression.ResultType.BOOLEAN, dynamicContext); assertEquals(expected, result); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java index 17746b27d..ffa773f76 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInnermostTest.java @@ -52,7 +52,7 @@ private static Stream provideValues() { // NOPMD - false positive @MethodSource("provideValues") void test(@NonNull String expectedValueMetapath, @NonNull String actualValuesMetapath) { DynamicContext dynamicContext = newDynamicContext(); - INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(getContext()); + INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(); ISequence expected = IMetapathExpression.compile(expectedValueMetapath, dynamicContext.getStaticContext()) diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java index 6a0647477..375d88863 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java @@ -66,7 +66,7 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { IStringItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) .evaluateAs( - MockedDocumentGenerator.generateDocumentNodeItem(getContext()), + MockedDocumentGenerator.generateDocumentNodeItem(), IMetapathExpression.ResultType.ITEM, dynamicContext); assertNotNull(result); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java index 5b9a376d9..0937e6a89 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNameTest.java @@ -65,7 +65,7 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { IStringItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) .evaluateAs( - MockedDocumentGenerator.generateDocumentNodeItem(getContext()), + MockedDocumentGenerator.generateDocumentNodeItem(), IMetapathExpression.ResultType.ITEM, dynamicContext); assertNotNull(result); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java index 632738974..23c3b2b52 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnNamespaceUriTest.java @@ -66,7 +66,7 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) { IStringItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) .evaluateAs( - MockedDocumentGenerator.generateDocumentNodeItem(getContext()), + MockedDocumentGenerator.generateDocumentNodeItem(), IMetapathExpression.ResultType.ITEM, dynamicContext); assertNotNull(result); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java index e54963f98..72e8791c9 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnOutermostTest.java @@ -59,7 +59,7 @@ private static Stream provideValues() { // NOPMD - false positive @MethodSource("provideValues") void test(@NonNull String expectedValueMetapath, @NonNull String actualValuesMetapath) { DynamicContext dynamicContext = newDynamicContext(); - INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(getContext()); + INodeItem node = MockedDocumentGenerator.generateDocumentNodeItem(); ISequence expected = IMetapathExpression.compile(expectedValueMetapath, dynamicContext.getStaticContext()) diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java index e8d4c330f..fabe74d4e 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRootTest.java @@ -55,7 +55,7 @@ private static Stream provideValues() { // NOPMD - false positive void test(@NonNull String metapath) { DynamicContext dynamicContext = newDynamicContext(); - INodeItem root = MockedDocumentGenerator.generateDocumentNodeItem(getContext()); + INodeItem root = MockedDocumentGenerator.generateDocumentNodeItem(); INodeItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext()) .evaluateAs(root, IMetapathExpression.ResultType.ITEM, dynamicContext); INodeItem rootResult diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/impl/MockedDocumentGenerator.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/impl/MockedDocumentGenerator.java index 3b1b39aa4..8ac532550 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/impl/MockedDocumentGenerator.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/impl/MockedDocumentGenerator.java @@ -14,51 +14,107 @@ import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; import gov.nist.secauto.metaschema.core.model.IFieldInstance; import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.IResourceLocation; +import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.testing.MockedModelTestSupport; - -import org.jmock.Mockery; +import gov.nist.secauto.metaschema.core.testing.model.MockedModelTestSupport; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Supports generating a simple Metapath node item-based document model for use + * in unit tests. + */ public class MockedDocumentGenerator { + /** + * The document base URI used by documents generated by the + * {@link #generateDocumentNodeItem()} method. + */ + @NonNull + public static final URI BASE_URI = ObjectUtils.notNull(URI.create("https://example.com/resource")); + /** + * The namespace used by Metaschema module definitions generated by the + * {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final String NS = "http://example.com/ns"; + /** + * The qualified name for the root node ({@code /Q{http://example.com/ns}root}) + * of a document generated by the {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final IEnhancedQName ROOT_QNAME = IEnhancedQName.of(NS, "root"); + /** + * The qualified name for the assembly child node + * ({@code /Q{http://example.com/ns}root/Q{http://example.com/ns}assembly}) of a + * document generated by the {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final IEnhancedQName ASSEMBLY_QNAME = IEnhancedQName.of(NS, "assembly"); + /** + * The qualified name for the field child node + * ({@code /Q{http://example.com/ns}root/Q{http://example.com/ns}field}) of a + * document generated by the {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final IEnhancedQName FIELD_QNAME = IEnhancedQName.of(NS, "field"); + /** + * The qualified name for the flag child node + * ({@code /Q{http://example.com/ns}root/Q{http://example.com/ns}assembly/@field-flag}) + * of a document generated by the {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final IEnhancedQName ASSEMBLY_FLAG_QNAME = IEnhancedQName.of("assembly-flag"); + /** + * The qualified name for the flag child node + * ({@code /Q{http://example.com/ns}root/Q{http://example.com/ns}field/@field-flag}) + * of a document generated by the {@link #generateDocumentNodeItem()} method. + */ @NonNull public static final IEnhancedQName FIELD_FLAG_QNAME = IEnhancedQName.of("field-flag"); - public static IDMDocumentNodeItem generateDocumentNodeItem(@NonNull Mockery context) { - MockedModelTestSupport mocking = new MockedModelTestSupport(context); - IResourceLocation resourceLocation = mocking.mock(IResourceLocation.class); + /** + * Generate a simple Metaschema-based document model. + * + * @return the document node item for this model + */ + public static IDMDocumentNodeItem generateDocumentNodeItem() { + MockedModelTestSupport mocking = new MockedModelTestSupport(); - IAssemblyDefinition rootDefinition = mocking.assembly().qname(ROOT_QNAME).rootQName(ROOT_QNAME).toDefinition(); - IAssemblyInstance assemblyInstance = mocking.assembly().qname(ASSEMBLY_QNAME).toInstance(rootDefinition); - IFlagInstance assemblyFlag = mocking.flag().qname(ASSEMBLY_FLAG_QNAME).toInstance(assemblyInstance.getDefinition()); - IFieldInstance fieldInstance = mocking.field().qname(FIELD_QNAME).toInstance(rootDefinition); - IFlagInstance fieldFlag = mocking.flag().qname(FIELD_FLAG_QNAME).toInstance(fieldInstance.getDefinition()); + ISource moduleSource = ISource.externalSource("https://example.com/module"); + + IAssemblyDefinition rootDefinition = mocking.assembly() + .qname(ROOT_QNAME) + .rootQName(ROOT_QNAME) + .source(moduleSource) + .toDefinition(); + IAssemblyInstance assemblyInstance = mocking.assembly() + .qname(ASSEMBLY_QNAME) + .source(moduleSource) + .toInstance(rootDefinition); + IFlagInstance assemblyFlag = mocking.flag() + .qname(ASSEMBLY_FLAG_QNAME) + .source(moduleSource) + .toInstance(assemblyInstance.getDefinition()); + IFieldInstance fieldInstance = mocking.field() + .qname(FIELD_QNAME) + .source(moduleSource) + .toInstance(rootDefinition); + IFlagInstance fieldFlag = mocking.flag() + .qname(FIELD_FLAG_QNAME) + .source(moduleSource) + .toInstance(fieldInstance.getDefinition()); IDMDocumentNodeItem document = IDMDocumentNodeItem.newInstance( - URI.create("https://example.com/resource"), - resourceLocation, - rootDefinition, - resourceLocation); + BASE_URI, + rootDefinition); IDMRootAssemblyNodeItem root = document.getRootAssemblyNodeItem(); - IDMAssemblyNodeItem assembly = root.newAssembly(assemblyInstance, resourceLocation); - assembly.newFlag(assemblyFlag, resourceLocation, IStringItem.valueOf("assembly-flag")); - IDMFieldNodeItem field = root.newField(fieldInstance, resourceLocation, IStringItem.valueOf("field")); - field.newFlag(fieldFlag, resourceLocation, IStringItem.valueOf("field-flag")); - + IDMAssemblyNodeItem assembly = root.newAssembly(assemblyInstance); + assembly.newFlag(assemblyFlag, IStringItem.valueOf("assembly-flag")); + IDMFieldNodeItem field = root.newField(fieldInstance, IStringItem.valueOf("field")); + field.newFlag(fieldFlag, IStringItem.valueOf("field-flag")); return document; } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/DefaultNodeItemFactoryTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/DefaultNodeItemFactoryTest.java index bd81cfab3..950cba715 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/DefaultNodeItemFactoryTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/DefaultNodeItemFactoryTest.java @@ -1,27 +1,22 @@ package gov.nist.secauto.metaschema.core.metapath.item.node; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.equalTo; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import gov.nist.secauto.metaschema.core.mdm.IDMAssemblyNodeItem; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.IFieldInstance; +import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.testing.MockedModelTestSupport; +import gov.nist.secauto.metaschema.core.testing.model.MockedModelTestSupport; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import org.hamcrest.FeatureMatcher; -import org.hamcrest.Matcher; -import org.jmock.Expectations; import org.junit.jupiter.api.Test; -import java.net.URI; import java.util.Collection; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; import edu.umd.cs.findbugs.annotations.NonNull; @@ -29,104 +24,41 @@ class DefaultNodeItemFactoryTest extends MockedModelTestSupport { @NonNull - private static final URI NS_URI = ObjectUtils.notNull(URI.create("http://example.com/ns")); - @NonNull - private static final String NS = ObjectUtils.notNull(NS_URI.toASCIIString()); - - @Test - void testGenerateFlags() { - DefaultNodeItemFactory nodeFactory = DefaultNodeItemFactory.instance(); - - IAssemblyDefinition parent = assembly() - .namespace(NS_URI) - .name("assembly1") - .toDefinition(); - - IFieldInstance fieldInstance = field() - .namespace(NS_URI) - .name("field1") - .flags(List.of( - flag().namespace(NS_URI).name("flag1"))) - .toInstance(parent); - - Object fieldValue = "test value"; - - // setup the value calls - getContext().checking(new Expectations() { - { // NOPMD - intentional - allowing(fieldInstance.getDefinition().getFlagInstanceByName(IEnhancedQName.of(NS, "flag1").getIndexPosition())) - .getValue(fieldValue); - will(returnValue("flag1 value")); - } - }); - - IAssemblyNodeItem parentItem = INodeItemFactory.instance().newAssemblyNodeItem(parent); - IFieldNodeItem field = new FieldInstanceNodeItemImpl(fieldInstance, parentItem, 2, fieldValue, nodeFactory); - - Collection flagItems = field.getFlags(); - assertThat(flagItems, containsInAnyOrder( - allOf( - match("name", IFlagNodeItem::getQName, equalTo(IEnhancedQName.of(NS, "flag1"))), - match("value", IFlagNodeItem::getValue, equalTo("flag1 value"))))); // NOPMD - } + private static final String NS = ObjectUtils.notNull("http://example.com/ns"); @Test void testGenerateModelItems() { IAssemblyDefinition assembly = assembly() - .namespace(NS_URI) + .namespace(NS) .name("assembly1") + .source(ISource.externalSource("http://example.com/module")) .flags(List.of( - flag().namespace(NS_URI).name("flag1"))) + flag().namespace(NS).name("flag1"))) .modelInstances(List.of( - field().namespace(NS_URI).name("field1"))) + field().namespace(NS).name("field1"))) .toDefinition(); - Object assemblyValue = "assembly value"; - Object flagValue = "flag1 value"; - Object fieldValue = "field1 value"; - // Setup the value calls - getContext().checking(new Expectations() { - { // NOPMD - intentional - allowing(assembly.getFlagInstanceByName(IEnhancedQName.of(NS, "flag1").getIndexPosition())) - .getValue(assemblyValue); - will(returnValue(flagValue)); - allowing(assembly.getNamedModelInstanceByName(IEnhancedQName.of(NS, "field1").getIndexPosition())) - .getValue(assemblyValue); - will(returnValue(fieldValue)); - allowing(assembly.getNamedModelInstanceByName(IEnhancedQName.of(NS, "field1").getIndexPosition())) - .getItemValues(fieldValue); - will(returnValue(List.of(fieldValue))); - } - }); - - IAssemblyNodeItem parentItem = INodeItemFactory.instance().newAssemblyNodeItem(assembly, null, assemblyValue); + StaticContext staticContext = StaticContext.instance(); + IDMAssemblyNodeItem parentItem = IDMAssemblyNodeItem.newInstance(assembly, staticContext); + assembly.getFlagInstances() + .forEach(flag -> parentItem.newFlag(flag, IStringItem.valueOf(flag.getName() + " value"))); + assembly.getFieldInstances() + .forEach(field -> { + parentItem.newField(field, IStringItem.valueOf(field.getName() + " value")); + }); Collection flagItems = parentItem.getFlags(); Collection> modelItems = parentItem.modelItems() .collect(Collectors.toUnmodifiableList()); assertAll( - () -> assertThat(flagItems, containsInAnyOrder( - allOf( - match("name", IFlagNodeItem::getQName, equalTo(IEnhancedQName.of(NS, "flag1"))), - match("value", IFlagNodeItem::getValue, equalTo("flag1 value"))))), - () -> assertThat(modelItems, containsInAnyOrder( - allOf( - match("name", IModelNodeItem::getQName, - equalTo(IEnhancedQName.of(NS, "field1"))), - match("value", IModelNodeItem::getValue, - equalTo("field1 value")))))); - } - - private static FeatureMatcher match( - @NonNull String label, - @NonNull Function lambda, - Matcher matcher) { - return new FeatureMatcher<>(matcher, label, label) { - @Override - protected R featureValueOf(T actual) { - return lambda.apply(actual); - } - }; + () -> assertThat(flagItems).extracting(IFlagNodeItem::getQName) + .isEqualTo(List.of(IEnhancedQName.of(NS, "flag1"))), + () -> assertThat(flagItems).extracting(flag -> flag.toAtomicItem().asString()) + .isEqualTo(List.of("flag1 value")), + () -> assertThat(modelItems).extracting(IModelNodeItem::getQName) + .isEqualTo(List.of(IEnhancedQName.of(NS, "field1"))), + () -> assertThat(modelItems).extracting(field -> field.toAtomicItem().asString()) + .isEqualTo(List.of("field1 value"))); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/MockNodeItemFactory.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/MockNodeItemFactory.java index aa6352f9a..10831650c 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/MockNodeItemFactory.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/node/MockNodeItemFactory.java @@ -3,26 +3,22 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.withSettings; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; import gov.nist.secauto.metaschema.core.model.IFieldDefinition; import gov.nist.secauto.metaschema.core.model.IFlagDefinition; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.testing.model.mocking.AbstractMockitoFactory; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import org.mockito.Answers; -import org.mockito.Mockito; - import java.net.URI; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import edu.umd.cs.findbugs.annotations.NonNull; @@ -31,20 +27,8 @@ // TODO: Integrate with classes in gov.nist.secauto.metaschema.core.testing @SuppressWarnings("checkstyle:MissingJavadocMethodCheck") @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") -public class MockNodeItemFactory { - @SuppressWarnings("null") - @NonNull - protected T newMock(@NonNull Class clazz, @NonNull String name) { - String mockName = new StringBuilder() - .append(clazz.getSimpleName()) - .append('-') - .append(name) - .append('-') - .append(UUID.randomUUID().toString()) - .toString(); - return Mockito.mock(clazz, withSettings().name(mockName).defaultAnswer(Answers.CALLS_REAL_METHODS)); - } - +public class MockNodeItemFactory + extends AbstractMockitoFactory { @NonNull public IDocumentNodeItem document( URI documentURI, @@ -52,9 +36,9 @@ public IDocumentNodeItem document( List flags, List> modelItems) { String qname = ObjectUtils.requireNonNull(rootName.toString()); - IDocumentNodeItem document = newMock(IDocumentNodeItem.class, qname); - IRootAssemblyNodeItem root = newMock(IRootAssemblyNodeItem.class, qname); - IAssemblyDefinition definition = newMock(IAssemblyDefinition.class, qname); + IDocumentNodeItem document = mock(IDocumentNodeItem.class, qname); + IRootAssemblyNodeItem root = mock(IRootAssemblyNodeItem.class, qname); + IAssemblyDefinition definition = mock(IAssemblyDefinition.class, qname); // doAnswer(invocation -> Stream.of(root)).when(document).modelItems(); doReturn(root).when(document).getRootAssemblyNodeItem(); @@ -136,8 +120,8 @@ protected void handleChildren( @NonNull public IFlagNodeItem flag(@NonNull IEnhancedQName name, @NonNull IAnyAtomicItem value) { - IFlagNodeItem flag = newMock(IFlagNodeItem.class, ObjectUtils.notNull(name.toString())); - IFlagDefinition definition = newMock(IFlagDefinition.class, ObjectUtils.notNull(name.toString())); + IFlagNodeItem flag = mock(IFlagNodeItem.class, ObjectUtils.notNull(name.toString())); + IFlagDefinition definition = mock(IFlagDefinition.class, ObjectUtils.notNull(name.toString())); doReturn(name).when(flag).getQName(); doReturn(true).when(flag).hasValue(); @@ -163,8 +147,8 @@ public IFieldNodeItem field( @NonNull IEnhancedQName name, @NonNull IAnyAtomicItem value, List flags) { - IFieldNodeItem field = newMock(IFieldNodeItem.class, ObjectUtils.notNull(name.toString())); - IFieldDefinition definition = newMock(IFieldDefinition.class, ObjectUtils.notNull(name.toString())); + IFieldNodeItem field = mock(IFieldNodeItem.class, ObjectUtils.notNull(name.toString())); + IFieldDefinition definition = mock(IFieldDefinition.class, ObjectUtils.notNull(name.toString())); doReturn(name).when(field).getQName(); doReturn(true).when(field).hasValue(); @@ -184,8 +168,8 @@ public IAssemblyNodeItem assembly( @NonNull IEnhancedQName name, List flags, List> modelItems) { - IAssemblyNodeItem assembly = newMock(IAssemblyNodeItem.class, ObjectUtils.notNull(name.toString())); - IAssemblyDefinition definition = newMock(IAssemblyDefinition.class, ObjectUtils.notNull(name.toString())); + IAssemblyNodeItem assembly = mock(IAssemblyNodeItem.class, ObjectUtils.notNull(name.toString())); + IAssemblyDefinition definition = mock(IAssemblyDefinition.class, ObjectUtils.notNull(name.toString())); doReturn(name).when(assembly).getQName(); doReturn(false).when(assembly).hasValue(); diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java new file mode 100644 index 000000000..54e3694a0 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/impl/AbstractConfigurableMessageConstraintTest.java @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.model.constraint.impl; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; +import gov.nist.secauto.metaschema.core.metapath.function.library.impl.MockedDocumentGenerator; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; +import gov.nist.secauto.metaschema.core.model.ISource; +import gov.nist.secauto.metaschema.core.model.constraint.DefaultConstraintValidator; +import gov.nist.secauto.metaschema.core.model.constraint.FindingCollectingConstraintValidationHandler; +import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.junit.jupiter.api.Test; + +import java.net.URI; + +class AbstractConfigurableMessageConstraintTest + extends ExpressionTestBase { + + @Test + void testDifferentNS() { + StaticContext constraintContext = StaticContext.builder() + .defaultModelNamespace(NS) + .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) + .build(); + + IExpectConstraint expect = IExpectConstraint.builder() + .target(IMetapathExpression.compile("assembly", constraintContext)) + .test(IMetapathExpression.compile("ancestor::root", constraintContext)) + .source(ISource.externalSource(constraintContext, false)) + .build(); + + IDocumentNodeItem document = MockedDocumentGenerator.generateDocumentNodeItem(); + document.getRootAssemblyNodeItem().getDefinition().addConstraint(expect); + + FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); + DefaultConstraintValidator validator = new DefaultConstraintValidator(handler); + validator.validate( + document, + new DynamicContext(document.getStaticContext())); + + assertAll( + () -> assertTrue(handler.isPassing()), + () -> assertEquals(0, handler.getFindings().size())); + } + + @Test + void testWildCard() { + StaticContext constraintContext = StaticContext.builder() + .defaultModelNamespace(NS) + .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) + .build(); + + IExpectConstraint expect = IExpectConstraint.builder() + .target(IMetapathExpression.compile("assembly", constraintContext)) + .test(IMetapathExpression.compile("ancestor::*:root", constraintContext)) + .source(ISource.externalSource(constraintContext, false)) + .build(); + + IDocumentNodeItem document = MockedDocumentGenerator.generateDocumentNodeItem(); + document.getRootAssemblyNodeItem().getDefinition().addConstraint(expect); + + FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); + DefaultConstraintValidator validator = new DefaultConstraintValidator(handler); + validator.validate( + document, + new DynamicContext(document.getStaticContext())); + + assertAll( + () -> assertTrue(handler.isPassing()), + () -> assertEquals(0, handler.getFindings().size())); + } + + @Test + void testPrefix() { + StaticContext constraintContext = StaticContext.builder() + .defaultModelNamespace(NS) + .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) + .namespace("ns", MockedDocumentGenerator.NS) + .build(); + + IExpectConstraint expect = IExpectConstraint.builder() + .target(IMetapathExpression.compile("ns:assembly", constraintContext)) + .test(IMetapathExpression.compile("ancestor::ns:root", constraintContext)) + .source(ISource.externalSource(constraintContext, false)) + .build(); + + IDocumentNodeItem document = MockedDocumentGenerator.generateDocumentNodeItem(); + document.getRootAssemblyNodeItem().getDefinition().addConstraint(expect); + + FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); + DefaultConstraintValidator validator = new DefaultConstraintValidator(handler); + validator.validate( + document, + new DynamicContext(document.getStaticContext())); + + assertAll( + () -> assertTrue(handler.isPassing()), + () -> assertEquals(0, handler.getFindings().size())); + } + + @Test + void testQualifiedName() { + StaticContext constraintContext = StaticContext.builder() + .defaultModelNamespace(NS) + .baseUri(ObjectUtils.notNull(URI.create("https://example.com/other"))) + .build(); + + IExpectConstraint expect = IExpectConstraint.builder() + .target(IMetapathExpression.compile("Q{" + MockedDocumentGenerator.NS + "}assembly", constraintContext)) + .test(IMetapathExpression.compile("ancestor::Q{" + MockedDocumentGenerator.NS + "}root", constraintContext)) + .source(ISource.externalSource(constraintContext, false)) + .build(); + + IDocumentNodeItem document = MockedDocumentGenerator.generateDocumentNodeItem(); + document.getRootAssemblyNodeItem().getDefinition().addConstraint(expect); + + FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); + DefaultConstraintValidator validator = new DefaultConstraintValidator(handler); + validator.validate( + document, + new DynamicContext(document.getStaticContext())); + + assertAll( + () -> assertTrue(handler.isPassing()), + () -> assertEquals(0, handler.getFindings().size())); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/AssemblyBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/AssemblyBuilder.java deleted file mode 100644 index 3f8da001d..000000000 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/AssemblyBuilder.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.testing; - -import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.IAssemblyInstanceAbsolute; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute; -import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.util.CollectionUtil; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.jmock.Expectations; -import org.jmock.Mockery; - -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; - -public final class AssemblyBuilder - extends AbstractModelBuilder - implements IModelInstanceBuilder { - - private String rootNamespace = ""; - private String rootName; - private List flags; - private List modelInstances; - - private AssemblyBuilder(@NonNull Mockery ctx) { - super(ctx); - } - - /** - * Create a new builder using the provided mocking context. - * - * @param ctx - * the mocking context - * @return the new builder - */ - @NonNull - public static AssemblyBuilder builder(@NonNull Mockery ctx) { - return new AssemblyBuilder(ctx).reset(); - } - - @Override - public AssemblyBuilder reset() { - this.flags = CollectionUtil.emptyList(); - this.modelInstances = CollectionUtil.emptyList(); - return this; - } - - /** - * Apply the provided root namespace for use by this builder. - * - * @param name - * the namespace to use - * @return this builder - */ - @NonNull - public AssemblyBuilder rootNamespace(@NonNull String name) { - this.rootNamespace = name; - return this; - } - - /** - * Apply the provided root namespace for use by this builder. - * - * @param name - * the namespace to use - * @return this builder - */ - @NonNull - public AssemblyBuilder rootNamespace(@NonNull URI name) { - return rootNamespace(ObjectUtils.notNull(name.toASCIIString())); - } - - /** - * Apply the provided root name for use by this builder. - * - * @param name - * the name to use - * @return this builder - */ - @NonNull - public AssemblyBuilder rootName(@NonNull String name) { - this.rootName = name; - return this; - } - - /** - * Apply the provided root qualified name for use by this builder. - * - * @param qname - * the qualified name to use - * @return this builder - */ - @NonNull - public AssemblyBuilder rootQName(@NonNull IEnhancedQName qname) { - this.rootName = qname.getLocalName(); - this.rootNamespace = qname.getNamespace(); - return this; - } - - /** - * Use the provided flag instances for built fields. - * - * @param flags - * the flags to use - * @return this builder - */ - public AssemblyBuilder flags(@Nullable List flags) { - this.flags = flags == null ? CollectionUtil.emptyList() : flags; - return this; - } - - /** - * Use the provided model instances for built fields. - * - * @param modelInstances - * the model instances to use - * @return this builder - */ - public AssemblyBuilder modelInstances(@Nullable List modelInstances) { - this.modelInstances = modelInstances == null ? CollectionUtil.emptyList() : modelInstances; - return this; - } - - @Override - @NonNull - public IAssemblyInstanceAbsolute toInstance( - @NonNull IAssemblyDefinition parent) { - IAssemblyDefinition def = toDefinition(); - return toInstance(parent, def); - } - - /** - * Build a mocked assembly instance, using the provided definition, as a child - * of the provided parent. - * - * @param parent - * the parent containing the new instance - * @param definition - * the definition to base the instance on - * @return the new mocked instance - */ - @NonNull - public IAssemblyInstanceAbsolute toInstance( - @NonNull IAssemblyDefinition parent, - @NonNull IAssemblyDefinition definition) { - validate(); - - IAssemblyInstanceAbsolute retval = mock(IAssemblyInstanceAbsolute.class); - applyNamedInstance(retval, definition, parent); - return retval; - } - - /** - * Build a mocked assembly definition. - * - * @return the new mocked definition - */ - @NonNull - public IAssemblyDefinition toDefinition() { - validate(); - - IAssemblyDefinition retval = mock(IAssemblyDefinition.class); - applyDefinition(retval); - - Map flags = this.flags.stream() - .map(builder -> builder.toInstance(retval)) - .collect(Collectors.toUnmodifiableMap( - IFlagInstance::getQName, - Function.identity())); - - Map modelInstances = this.modelInstances.stream() - .map(builder -> builder.toInstance(retval)) - .collect(Collectors.toUnmodifiableMap( - INamedModelInstanceAbsolute::getQName, - Function.identity())); - - getContext().checking(new Expectations() { - { - if (rootName != null) { - allowing(retval).getRootQName(); - will(returnValue(IEnhancedQName.of(ObjectUtils.notNull(rootNamespace), ObjectUtils.notNull(rootName)))); - } - - allowing(retval).getFlagInstances(); - will(returnValue(flags.values())); - flags.forEach((key, value) -> { - allowing(retval).getFlagInstanceByName(with(key.getIndexPosition())); - will(returnValue(value)); - }); - allowing(retval).getModelInstances(); - will(returnValue(modelInstances.values())); - modelInstances.forEach((key, value) -> { - allowing(retval).getNamedModelInstanceByName(with(key.getIndexPosition())); - will(returnValue(value)); - }); - } - }); - - return retval; - } -} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FieldBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FieldBuilder.java deleted file mode 100644 index 25adead9f..000000000 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FieldBuilder.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.testing; - -import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.IFieldDefinition; -import gov.nist.secauto.metaschema.core.model.IFieldInstanceAbsolute; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; -import gov.nist.secauto.metaschema.core.util.CollectionUtil; - -import org.jmock.Expectations; -import org.jmock.Mockery; - -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; - -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; - -public final class FieldBuilder - extends AbstractModelBuilder - implements IModelInstanceBuilder { - - private IDataTypeAdapter dataTypeAdapter; - private Object defaultValue = null; - private List flags; - - private FieldBuilder(@NonNull Mockery ctx) { - super(ctx); - } - - /** - * Create a new builder using the provided mocking context. - * - * @param ctx - * the mocking context - * @return the new builder - */ - @NonNull - public static FieldBuilder builder(@NonNull Mockery ctx) { - return new FieldBuilder(ctx).reset(); - } - - @Override - public FieldBuilder reset() { - this.dataTypeAdapter = MetaschemaDataTypeProvider.DEFAULT_DATA_TYPE; - this.defaultValue = null; - this.flags = CollectionUtil.emptyList(); - return this; - } - - /** - * Apply the provided data type adapter to built fields. - * - * @param dataTypeAdapter - * the data type adapter to use - * @return this builder - */ - public FieldBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter) { - this.dataTypeAdapter = dataTypeAdapter; - return this; - } - - /** - * Apply the provided data type adapter to built fields. - * - * @param defaultValue - * the default value to use - * @return this builder - */ - public FieldBuilder defaultValue(@NonNull Object defaultValue) { - this.defaultValue = defaultValue; - return this; - } - - /** - * Use the provided flag instances for built fields. - * - * @param flags - * the flags to use - * @return this builder - */ - public FieldBuilder flags(@Nullable List flags) { - this.flags = flags == null ? CollectionUtil.emptyList() : flags; - return this; - } - - /** - * Build a mocked field instance, based on a mocked definition, as a child of - * the provided parent. - * - * @param parent - * the parent containing the new instance - * @return the new mocked instance - */ - @Override - @NonNull - public IFieldInstanceAbsolute toInstance( - @NonNull IAssemblyDefinition parent) { - IFieldDefinition def = toDefinition(); - return toInstance(parent, def); - } - - /** - * Build a mocked field instance, using the provided definition, as a child of - * the provided parent. - * - * @param parent - * the parent containing the new instance - * @param definition - * the definition to base the instance on - * @return the new mocked instance - */ - @NonNull - public IFieldInstanceAbsolute toInstance( - @NonNull IAssemblyDefinition parent, - @NonNull IFieldDefinition definition) { - validate(); - - IFieldInstanceAbsolute retval = mock(IFieldInstanceAbsolute.class); - applyNamedInstance(retval, definition, parent); - return retval; - } - - /** - * Build a mocked field definition. - * - * @return the new mocked definition - */ - @NonNull - public IFieldDefinition toDefinition() { - validate(); - - IFieldDefinition retval = mock(IFieldDefinition.class); - applyDefinition(retval); - - Map flags = this.flags.stream() - .map(builder -> builder.toInstance(retval)) - .collect(Collectors.toUnmodifiableMap( - IFlagInstance::getQName, - Function.identity())); - - getContext().checking(new Expectations() { - { - allowing(retval).getJavaTypeAdapter(); - will(returnValue(dataTypeAdapter)); - allowing(retval).getDefaultValue(); - will(returnValue(defaultValue)); - allowing(retval).getFlagInstances(); - will(returnValue(flags.values())); - flags.forEach((key, value) -> { - allowing(retval).getFlagInstanceByName(with(key.getIndexPosition())); - will(returnValue(value)); - }); - } - }); - - return retval; - } -} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FlagBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FlagBuilder.java deleted file mode 100644 index 32c902b77..000000000 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/FlagBuilder.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.testing; - -import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.model.IFlagDefinition; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; -import gov.nist.secauto.metaschema.core.model.IModelDefinition; - -import org.jmock.Expectations; -import org.jmock.Mockery; - -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * A builder that generates mock flag definitions and instances. - */ -public final class FlagBuilder - extends AbstractModelBuilder { - - private IDataTypeAdapter dataTypeAdapter; - private Object defaultValue = null; - private boolean required; - - private FlagBuilder(@NonNull Mockery ctx) { - super(ctx); - } - - /** - * Create a new builder using the provided mocking context. - * - * @param ctx - * the mocking context - * @return the new builder - */ - @NonNull - public static FlagBuilder builder(@NonNull Mockery ctx) { - return new FlagBuilder(ctx).reset(); - } - - @Override - public FlagBuilder reset() { - this.dataTypeAdapter = MetaschemaDataTypeProvider.DEFAULT_DATA_TYPE; - this.defaultValue = null; - this.required = IFlagInstance.DEFAULT_FLAG_REQUIRED; - return this; - } - - /** - * Apply the provided required setting to built flags. - * - * @param required - * {@code true} if the flag is required or {@code false} otherwise - * @return this builder - */ - public FlagBuilder required(boolean required) { - this.required = required; - return this; - } - - /** - * Apply the provided data type adapter to built flags. - * - * @param dataTypeAdapter - * the data type adapter to use - * @return this builder - */ - public FlagBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter) { - this.dataTypeAdapter = dataTypeAdapter; - return this; - } - - /** - * Apply the provided data type adapter to built flags. - * - * @param defaultValue - * the default value to use - * @return this builder - */ - public FlagBuilder defaultValue(@NonNull Object defaultValue) { - this.defaultValue = defaultValue; - return this; - } - - /** - * Build a mocked flag instance, based on a mocked definition, as a child of the - * provided parent. - * - * @param parent - * the parent containing the new instance - * @return the new mocked instance - */ - @NonNull - public IFlagInstance toInstance(@NonNull IModelDefinition parent) { - IFlagDefinition def = toDefinition(); - return toInstance(parent, def); - } - - /** - * Build a mocked flag instance, using the provided definition, as a child of - * the provided parent. - * - * @param parent - * the parent containing the new instance - * @param definition - * the definition to base the instance on - * @return the new mocked instance - */ - @NonNull - public IFlagInstance toInstance( - @NonNull IModelDefinition parent, - @NonNull IFlagDefinition definition) { - validate(); - - IFlagInstance retval = mock(IFlagInstance.class); - - applyNamedInstance(retval, definition, parent); - - getContext().checking(new Expectations() { - { - allowing(retval).isRequired(); - will(returnValue(required)); - } - }); - - return retval; - } - - /** - * Build a mocked flag definition. - * - * @return the new mocked definition - */ - @NonNull - public IFlagDefinition toDefinition() { - validate(); - - IFlagDefinition retval = mock(IFlagDefinition.class); - applyDefinition(retval); - - getContext().checking(new Expectations() { - { - allowing(retval).getJavaTypeAdapter(); - will(returnValue(dataTypeAdapter)); - allowing(retval).getDefaultValue(); - will(returnValue(defaultValue)); - } - }); - return retval; - } -} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModelInstanceBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModelInstanceBuilder.java deleted file mode 100644 index 39bba6bd2..000000000 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModelInstanceBuilder.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.testing; - -import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; -import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public interface IModelInstanceBuilder { - /** - * Generate an instance of the implementing type using the provided parent. - * - * @param parent - * the parent assembly definition containing the instance - * @return the new instance - */ - @NonNull - INamedModelInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent); -} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockedModelTestSupport.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockedModelTestSupport.java deleted file mode 100644 index 96d17b74b..000000000 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockedModelTestSupport.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.testing; - -import org.jmock.Mockery; -import org.jmock.junit5.JUnit5Mockery; -import org.junit.jupiter.api.extension.RegisterExtension; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class MockedModelTestSupport implements IModuleMockFactory { - @RegisterExtension - @NonNull - Mockery context; - - /** - * Construct a new model mock factory using the default JUnit-based mocking - * context. - */ - public MockedModelTestSupport() { - this(new JUnit5Mockery()); - } - - /** - * Construct a new model mock factory using the provided mocking context. - */ - public MockedModelTestSupport(@NonNull Mockery context) { - this.context = context; - } - - @Override - public Mockery getContext() { - return context; - } - - @Override - public T mock(Class clazz, String name) { - return new MockFactory(getContext()).mock(clazz, name); - } -} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/AbstractModelBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractMetaschemaBuilder.java similarity index 52% rename from core/src/test/java/gov/nist/secauto/metaschema/core/testing/AbstractModelBuilder.java rename to core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractMetaschemaBuilder.java index 67c2a47cf..fd1fbe737 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/AbstractModelBuilder.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractMetaschemaBuilder.java @@ -3,7 +3,9 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.testing; +package gov.nist.secauto.metaschema.core.testing.model; + +import static org.mockito.Mockito.doReturn; import gov.nist.secauto.metaschema.core.model.IAttributable; import gov.nist.secauto.metaschema.core.model.IDefinition; @@ -11,15 +13,12 @@ import gov.nist.secauto.metaschema.core.model.IModelElement; import gov.nist.secauto.metaschema.core.model.INamedInstance; import gov.nist.secauto.metaschema.core.model.INamedModelElement; +import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.testing.model.mocking.AbstractMockitoFactory; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import org.jmock.Expectations; -import org.jmock.Mockery; - -import java.net.URI; - import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -28,89 +27,64 @@ * @param * the Java type of this builder */ -public abstract class AbstractModelBuilder> - extends MockFactory { +abstract class AbstractMetaschemaBuilder> + extends AbstractMockitoFactory + implements IMetaschemaBuilder { private String namespace = ""; private String name; + private ISource source; /** - * Construct a new builder using the provided mocking context. - * - * @param ctx - * the mocking context + * Construct a new builder. */ - protected AbstractModelBuilder(@NonNull Mockery ctx) { - super(ctx); + protected AbstractMetaschemaBuilder() { + // allow extending classes to construct } - /** - * Reset the builder back to a default state. - * - * @return this builder - */ - @NonNull - @SuppressWarnings("unchecked") + @Override public T reset() { this.name = null; this.namespace = null; - return (T) this; + return ObjectUtils.asType(this); } - /** - * Apply the provided namespace for use by this builder. - * - * @param name - * the namespace to use - * @return this builder - */ - @SuppressWarnings("unchecked") + @Override @NonNull public T namespace(@NonNull String name) { this.namespace = name; - return (T) this; + return ObjectUtils.asType(this); } - /** - * Apply the provided namespace for use by this builder. - * - * @param name - * the namespace to use - * @return this builder - */ - @SuppressWarnings("unchecked") - @NonNull - public T namespace(@NonNull URI name) { - return namespace(name.toASCIIString()); - } - - /** - * Apply the provided name for use by this builder. - * - * @param name - * the name to use - * @return this builder - */ - @SuppressWarnings("unchecked") + @Override @NonNull public T name(@NonNull String name) { this.name = name; - return (T) this; + return ObjectUtils.asType(this); } - /** - * Apply the provided qualified name for use by this builder. - * - * @param qname - * the qualified name to use - * @return this builder - */ - @SuppressWarnings("unchecked") + @Override @NonNull public T qname(@NonNull IEnhancedQName qname) { this.name = qname.getLocalName(); this.namespace = qname.getNamespace(); - return (T) this; + return ObjectUtils.asType(this); + } + + @Override + @NonNull + public T source(@NonNull ISource source) { + this.source = source; + return ObjectUtils.asType(this); + } + + /** + * Get the currently configured source. + * + * @return the source or {@code null} if no source is configured + */ + protected ISource getSource() { + return source; } /** @@ -119,6 +93,7 @@ public T qname(@NonNull IEnhancedQName qname) { */ protected void validate() { ObjectUtils.requireNonEmpty(name, "name"); + ObjectUtils.requireNonNull(source, "source"); } /** @@ -131,12 +106,17 @@ protected void applyDefinition(@NonNull IDefinition definition) { applyModelElement(definition); applyNamed(definition); applyAttributable(definition); - getContext().checking(new Expectations() { - { - allowing(definition).getDefinitionQName(); - will(returnValue(IEnhancedQName.of(ObjectUtils.notNull(namespace), ObjectUtils.notNull(name)))); - } - }); + + IEnhancedQName qname = IEnhancedQName.of(ObjectUtils.notNull(namespace), ObjectUtils.notNull(name)); + + doReturn(qname).when(definition).getDefinitionQName(); + doReturn(null).when(definition).getRemarks(); + doReturn(CollectionUtil.emptyMap()).when(definition).getProperties(); + doReturn(null).when(definition).getInlineInstance(); + + // doReturn().when(definition).getConstraintSupport(); + // doReturn().when(definition).getContainingModule(); + // doReturn().when(definition).getModelType(); } /** @@ -159,18 +139,11 @@ protected void applyNamedInstance( applyModelElement(instance); applyNamed(instance); applyAttributable(instance); - getContext().checking(new Expectations() { - { - allowing(instance).getQName(); - will(returnValue(IEnhancedQName.of(ObjectUtils.notNull(namespace), ObjectUtils.notNull(name)))); - allowing(instance).getDefinition(); - will(returnValue(definition)); - allowing(instance).getContainingDefinition(); - will(returnValue(parent)); - allowing(instance).getParentContainer(); - will(returnValue(parent)); - } - }); + + doReturn(name).when(instance).getName(); + doReturn(definition).when(instance).getDefinition(); + doReturn(parent).when(instance).getContainingDefinition(); + doReturn(parent).when(instance).getParentContainer(); } /** @@ -181,22 +154,12 @@ protected void applyNamedInstance( * the named model element to apply mocking expectations for */ protected void applyNamed(@NonNull INamedModelElement element) { - getContext().checking(new Expectations() { - { - allowing(element).getName(); - will(returnValue(name)); - allowing(element).getUseName(); - will(returnValue(null)); - allowing(element).getQName(); - will(returnValue(IEnhancedQName.of(ObjectUtils.notNull(namespace), ObjectUtils.notNull(name)))); - allowing(element).getEffectiveName(); - will(returnValue(name)); - allowing(element).getFormalName(); - will(returnValue(null)); - allowing(element).getDescription(); - will(returnValue(null)); - } - }); + IEnhancedQName qname = IEnhancedQName.of(ObjectUtils.notNull(namespace), ObjectUtils.notNull(name)); + + doReturn(qname).when(element).getQName(); + doReturn(name).when(element).getName(); + doReturn(null).when(element).getFormalName(); + doReturn(null).when(element).getDescription(); } /** @@ -207,12 +170,7 @@ protected void applyNamed(@NonNull INamedModelElement element) { * the element to apply mocking expectations for */ protected void applyAttributable(@NonNull IAttributable element) { - getContext().checking(new Expectations() { - { - allowing(element).getProperties(); - will(returnValue(CollectionUtil.emptyMap())); - } - }); + doReturn(CollectionUtil.emptyMap()).when(element).getProperties(); } /** @@ -222,11 +180,6 @@ protected void applyAttributable(@NonNull IAttributable element) { * the model element to apply mocking expectations for */ protected void applyModelElement(@NonNull IModelElement element) { - getContext().checking(new Expectations() { - { - allowing(element).getRemarks(); - will(returnValue(null)); - } - }); + doReturn(null).when(element).getRemarks(); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractModelBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractModelBuilder.java new file mode 100644 index 000000000..9be8f1a87 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AbstractModelBuilder.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * A base class for Metaschema module-based model builders for model elements + * that support flag children. + * + * @param + * the Java type of this builder + */ +abstract class AbstractModelBuilder> + extends AbstractMetaschemaBuilder { + private List flags = CollectionUtil.emptyList(); + + @Override + public T reset() { + super.reset(); + this.flags = CollectionUtil.emptyList(); + return ObjectUtils.asType(this); + } + + /** + * Get the configured flags. + * + * @return the list of flag builders + */ + protected List getFlags() { + return flags; + } + + /** + * Use the provided flag instances for definitions produced by this builder. + * + * @param flags + * the flags to use + * @return this builder + */ + public T flags(@Nullable List flags) { + this.flags = flags == null ? CollectionUtil.emptyList() : flags; + return ObjectUtils.asType(this); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AssemblyBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AssemblyBuilder.java new file mode 100644 index 000000000..6620c8a2f --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/AssemblyBuilder.java @@ -0,0 +1,184 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; + +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.IAssemblyInstance; +import gov.nist.secauto.metaschema.core.model.IAssemblyInstanceAbsolute; +import gov.nist.secauto.metaschema.core.model.IFieldInstance; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.model.INamedModelElement; +import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute; +import gov.nist.secauto.metaschema.core.model.ISource; +import gov.nist.secauto.metaschema.core.model.ModelType; +import gov.nist.secauto.metaschema.core.model.constraint.AssemblyConstraintSet; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +final class AssemblyBuilder + extends AbstractModelBuilder + implements IAssemblyBuilder { + + private String rootNamespace = ""; + private String rootName; + + private List> modelInstances; + + AssemblyBuilder() { + // prevent direct instantiation + } + + @Override + public AssemblyBuilder reset() { + super.reset(); + this.modelInstances = CollectionUtil.emptyList(); + return this; + } + + @Override + @NonNull + public AssemblyBuilder rootNamespace(@NonNull String name) { + this.rootNamespace = name; + return this; + } + + @Override + @NonNull + public AssemblyBuilder rootName(@NonNull String name) { + this.rootName = name; + return this; + } + + @Override + @NonNull + public AssemblyBuilder rootQName(@NonNull IEnhancedQName qname) { + this.rootName = qname.getLocalName(); + this.rootNamespace = qname.getNamespace(); + return this; + } + + @Override + public AssemblyBuilder modelInstances(@Nullable List> modelInstances) { + this.modelInstances = modelInstances == null ? CollectionUtil.emptyList() : modelInstances; + return this; + } + + @Override + @NonNull + public IAssemblyInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent) { + IAssemblyDefinition def = toDefinition(); + return toInstance(parent, def); + } + + /** + * Build a mocked assembly instance, using the provided definition, as a child + * of the provided parent. + * + * @param parent + * the parent containing the new instance + * @param definition + * the definition to base the instance on + * @return the new mocked instance + */ + @Override + @NonNull + public IAssemblyInstanceAbsolute toInstance( + @NonNull IAssemblyDefinition parent, + @NonNull IAssemblyDefinition definition) { + validate(); + + IAssemblyInstanceAbsolute retval = mock(IAssemblyInstanceAbsolute.class); + applyNamedInstance(retval, definition, parent); + return retval; + } + + /** + * Build a mocked assembly definition. + * + * @return the new mocked definition + */ + @Override + @NonNull + public IAssemblyDefinition toDefinition() { + validate(); + + // already validated as non-null + ISource source = ObjectUtils.notNull(getSource()); + + IAssemblyDefinition retval = mock(IAssemblyDefinition.class); + applyDefinition(retval); + + Map flags = getFlags().stream() + .map(builder -> builder.source(source).toInstance(retval)) + .collect(Collectors.toUnmodifiableMap( + IFlagInstance::getQName, + Function.identity())); + + if (rootName != null) { + doReturn(ModelType.ASSEMBLY).when(retval).getModelType(); + + IEnhancedQName rootQName = IEnhancedQName.of(ObjectUtils.notNull(rootNamespace), ObjectUtils.notNull(rootName)); + doReturn(rootQName).when(retval).getRootQName(); + } + + doReturn(new AssemblyConstraintSet(source)).when(retval).getConstraintSupport(); + + doReturn(flags.values()).when(retval).getFlagInstances(); + flags.entrySet().forEach(entry -> { + assert entry != null; + doReturn(entry.getValue()).when(retval).getFlagInstanceByName(eq(entry.getKey().getIndexPosition())); + }); + + Map modelInstances = this.modelInstances.stream() + .map(builder -> builder.source(source).toInstance(retval)) + .collect(Collectors.toUnmodifiableMap( + INamedModelInstanceAbsolute::getQName, + Function.identity())); + + doReturn(modelInstances.values()).when(retval).getModelInstances(); + doReturn(CollectionUtil.emptyMap()).when(retval).getChoiceGroupInstances(); + doReturn(CollectionUtil.emptyList()).when(retval).getChoiceInstances(); + modelInstances.forEach((key, value) -> { + doReturn(value).when(retval).getNamedModelInstanceByName(eq(key.getIndexPosition())); + + if (value instanceof IAssemblyInstance) { + doReturn(value).when(retval).getAssemblyInstanceByName(eq(key.getIndexPosition())); + } else if (value instanceof IFieldInstance) { + doReturn(value).when(retval).getFieldInstanceByName(eq(key.getIndexPosition())); + } + }); + doReturn( + modelInstances.values().stream() + .flatMap(value -> value instanceof IAssemblyInstance ? Stream.of(value) : null) + .collect(Collectors.toList())) + .when(retval).getAssemblyInstances(); + doReturn( + modelInstances.values().stream() + .flatMap(value -> value instanceof IFieldInstance ? Stream.of(value) : null) + .collect(Collectors.toList())) + .when(retval).getFieldInstances(); + return retval; + } + + @Override + protected void applyNamed(INamedModelElement element) { + super.applyNamed(element); + doReturn(ModelType.ASSEMBLY).when(element).getModelType(); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FieldBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FieldBuilder.java new file mode 100644 index 000000000..d5bdbb25c --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FieldBuilder.java @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; + +import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldInstanceAbsolute; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.model.INamedModelElement; +import gov.nist.secauto.metaschema.core.model.ModelType; +import gov.nist.secauto.metaschema.core.model.constraint.ValueConstraintSet; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import edu.umd.cs.findbugs.annotations.NonNull; + +final class FieldBuilder + extends AbstractModelBuilder + implements IFieldBuilder { + + private IDataTypeAdapter dataTypeAdapter; + private Object defaultValue = null; + + FieldBuilder() { + // prevent direct instantiation + } + + @Override + public FieldBuilder reset() { + this.dataTypeAdapter = MetaschemaDataTypeProvider.DEFAULT_DATA_TYPE; + this.defaultValue = null; + return this; + } + + @Override + public FieldBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter) { + this.dataTypeAdapter = dataTypeAdapter; + return this; + } + + @Override + public FieldBuilder defaultValue(@NonNull Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + @Override + @NonNull + public IFieldInstanceAbsolute toInstance( + @NonNull IAssemblyDefinition parent) { + IFieldDefinition def = toDefinition(); + return toInstance(parent, def); + } + + @Override + @NonNull + public IFieldInstanceAbsolute toInstance( + @NonNull IAssemblyDefinition parent, + @NonNull IFieldDefinition definition) { + validate(); + + IFieldInstanceAbsolute retval = mock(IFieldInstanceAbsolute.class); + applyNamedInstance(retval, definition, parent); + return retval; + } + + @Override + @NonNull + public IFieldDefinition toDefinition() { + validate(); + + IFieldDefinition retval = mock(IFieldDefinition.class); + applyDefinition(retval); + + Map flags = getFlags().stream() + .map(builder -> builder.toInstance(retval)) + .collect(Collectors.toUnmodifiableMap( + IFlagInstance::getQName, + Function.identity())); + + doReturn(new ValueConstraintSet(ObjectUtils.notNull(getSource()))).when(retval).getConstraintSupport(); + doReturn(dataTypeAdapter).when(retval).getJavaTypeAdapter(); + doReturn(defaultValue).when(retval).getDefaultValue(); + + doReturn(flags.values()).when(retval).getFlagInstances(); + flags.entrySet().forEach(entry -> { + assert entry != null; + doReturn(entry.getValue()).when(retval).getFlagInstanceByName(eq(entry.getKey().getIndexPosition())); + }); + + return retval; + } + + @Override + protected void applyNamed(INamedModelElement element) { + super.applyNamed(element); + doReturn(ModelType.FIELD).when(element).getModelType(); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FlagBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FlagBuilder.java new file mode 100644 index 000000000..ae994784b --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/FlagBuilder.java @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import static org.mockito.Mockito.doReturn; + +import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.model.IFlagDefinition; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.model.IModelDefinition; +import gov.nist.secauto.metaschema.core.model.INamedModelElement; +import gov.nist.secauto.metaschema.core.model.ModelType; +import gov.nist.secauto.metaschema.core.model.constraint.ValueConstraintSet; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A builder that generates mock flag definitions and instances. + */ +final class FlagBuilder + extends AbstractMetaschemaBuilder + implements IFlagBuilder { + + private IDataTypeAdapter dataTypeAdapter; + private Object defaultValue = null; + private boolean required; + + FlagBuilder() { + // prevent direct instantiation + } + + @Override + public FlagBuilder reset() { + this.dataTypeAdapter = MetaschemaDataTypeProvider.DEFAULT_DATA_TYPE; + this.defaultValue = null; + this.required = IFlagInstance.DEFAULT_FLAG_REQUIRED; + return this; + } + + @Override + public FlagBuilder required(boolean required) { + this.required = required; + return this; + } + + @Override + public FlagBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter) { + this.dataTypeAdapter = dataTypeAdapter; + return this; + } + + @Override + public FlagBuilder defaultValue(@NonNull Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + @Override + @NonNull + public IFlagInstance toInstance( + @NonNull IModelDefinition parent, + @NonNull IFlagDefinition definition) { + validate(); + + IFlagInstance retval = mock(IFlagInstance.class); + + applyNamedInstance(retval, definition, parent); + + doReturn(required).when(retval).isRequired(); + return retval; + } + + /** + * Build a mocked flag definition. + * + * @return the new mocked definition + */ + @Override + @NonNull + public IFlagDefinition toDefinition() { + validate(); + + IFlagDefinition retval = mock(IFlagDefinition.class); + applyDefinition(retval); + + doReturn(new ValueConstraintSet(ObjectUtils.notNull(getSource()))).when(retval).getConstraintSupport(); + doReturn(dataTypeAdapter).when(retval).getJavaTypeAdapter(); + doReturn(defaultValue).when(retval).getDefaultValue(); + + return retval; + } + + @Override + protected void applyNamed(INamedModelElement element) { + super.applyNamed(element); + doReturn(ModelType.FLAG).when(element).getModelType(); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IAssemblyBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IAssemblyBuilder.java new file mode 100644 index 000000000..ba5e8bfc7 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IAssemblyBuilder.java @@ -0,0 +1,110 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.IAssemblyInstanceAbsolute; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.net.URI; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * Represents a Metaschema module-based model builder for producing assembly + * definitions and instances. + */ +public interface IAssemblyBuilder extends IModelBuilder { + + /** + * Create a new builder using the provided mocking context. + * + * @return the new builder + */ + @NonNull + static IAssemblyBuilder builder() { + return new AssemblyBuilder().reset(); + } + + /** + * Apply the provided root namespace for use by this builder. + * + * @param name + * the namespace to use + * @return this builder + */ + @NonNull + IAssemblyBuilder rootNamespace(@NonNull String name); + + /** + * Apply the provided root namespace for use by this builder. + * + * @param name + * the namespace to use + * @return this builder + */ + @NonNull + default IAssemblyBuilder rootNamespace(@NonNull URI name) { + return rootNamespace(ObjectUtils.notNull(name.toASCIIString())); + } + + /** + * Apply the provided root name for use by this builder. + * + * @param name + * the name to use + * @return this builder + */ + @NonNull + IAssemblyBuilder rootName(@NonNull String name); + + /** + * Apply the provided root qualified name for use by this builder. + * + * @param qname + * the qualified name to use + * @return this builder + */ + @NonNull + IAssemblyBuilder rootQName(@NonNull IEnhancedQName qname); + + /** + * Use the provided model instances for built fields. + * + * @param modelInstances + * the model instances to use + * @return this builder + */ + IAssemblyBuilder modelInstances(@Nullable List> modelInstances); + + @Override + @NonNull + IAssemblyInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent); + + /** + * Build a mocked assembly instance, using the provided definition, as a child + * of the provided parent. + * + * @param parent + * the parent containing the new instance + * @param definition + * the definition to base the instance on + * @return the new mocked instance + */ + @NonNull + IAssemblyInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent, @NonNull IAssemblyDefinition definition); + + /** + * Build a mocked assembly definition. + * + * @return the new mocked definition + */ + @NonNull + IAssemblyDefinition toDefinition(); +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFieldBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFieldBuilder.java new file mode 100644 index 000000000..f7822f29d --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFieldBuilder.java @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldDefinition; +import gov.nist.secauto.metaschema.core.model.IFieldInstanceAbsolute; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a Metaschema module-based model builder for producing field + * definitions and instances. + */ +public interface IFieldBuilder extends IModelBuilder { + + /** + * Create a new builder using the provided mocking context. + * + * @return the new builder + */ + @NonNull + static IFieldBuilder builder() { + return new FieldBuilder().reset(); + } + + /** + * Apply the provided data type adapter to built fields. + * + * @param dataTypeAdapter + * the data type adapter to use + * @return this builder + */ + IFieldBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter); + + /** + * Apply the provided data type adapter to built fields. + * + * @param defaultValue + * the default value to use + * @return this builder + */ + IFieldBuilder defaultValue(@NonNull Object defaultValue); + + /** + * Build a mocked field instance, based on a mocked definition, as a child of + * the provided parent. + * + * @param parent + * the parent containing the new instance + * @return the new mocked instance + */ + @Override + @NonNull + IFieldInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent); + + /** + * Build a mocked field instance, using the provided definition, as a child of + * the provided parent. + * + * @param parent + * the parent containing the new instance + * @param definition + * the definition to base the instance on + * @return the new mocked instance + */ + @NonNull + IFieldInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent, @NonNull IFieldDefinition definition); + + /** + * Build a mocked field definition. + * + * @return the new mocked definition + */ + @NonNull + IFieldDefinition toDefinition(); +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFlagBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFlagBuilder.java new file mode 100644 index 000000000..d4b7157c0 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IFlagBuilder.java @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.model.IFlagDefinition; +import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.model.IModelDefinition; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a Metaschema module-based model builder for producing flag + * definitions and instances. + */ +public interface IFlagBuilder extends IMetaschemaBuilder { + + /** + * Create a new builder using the provided mocking context. + * + * @return the new builder + */ + @NonNull + static IFlagBuilder builder() { + return new FlagBuilder().reset(); + } + + /** + * Apply the provided required setting to built flags. + * + * @param required + * {@code true} if the flag is required or {@code false} otherwise + * @return this builder + */ + IFlagBuilder required(boolean required); + + /** + * Apply the provided data type adapter to built flags. + * + * @param dataTypeAdapter + * the data type adapter to use + * @return this builder + */ + IFlagBuilder dataTypeAdapter(@NonNull IDataTypeAdapter dataTypeAdapter); + + /** + * Apply the provided data type adapter to built flags. + * + * @param defaultValue + * the default value to use + * @return this builder + */ + IFlagBuilder defaultValue(@NonNull Object defaultValue); + + /** + * Build a mocked flag instance, based on a mocked definition, as a child of the + * provided parent. + * + * @param parent + * the parent containing the new instance + * @return the new mocked instance + */ + @NonNull + default IFlagInstance toInstance(@NonNull IModelDefinition parent) { + IFlagDefinition def = toDefinition(); + return toInstance(parent, def); + } + + /** + * Build a mocked flag instance, using the provided definition, as a child of + * the provided parent. + * + * @param parent + * the parent containing the new instance + * @param definition + * the definition to base the instance on + * @return the new mocked instance + */ + @NonNull + IFlagInstance toInstance(@NonNull IModelDefinition parent, @NonNull IFlagDefinition definition); + + /** + * Build a mocked flag definition. + * + * @return the new mocked definition + */ + @NonNull + IFlagDefinition toDefinition(); +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IMetaschemaBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IMetaschemaBuilder.java new file mode 100644 index 000000000..5e32c71f2 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IMetaschemaBuilder.java @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.model.ISource; +import gov.nist.secauto.metaschema.core.qname.IEnhancedQName; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.net.URI; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a Metaschema module-based model builder. + * + * @param + * the Java type of the implementation of this builder + */ +public interface IMetaschemaBuilder> { + /** + * Reset the builder back to a default state. + * + * @return this builder + */ + @NonNull + T reset(); + + /** + * Apply the provided namespace for use by this builder. + * + * @param name + * the namespace to use + * @return this builder + */ + @NonNull + T namespace(@NonNull String name); + + /** + * Apply the provided namespace for use by this builder. + * + * @param name + * the namespace to use + * @return this builder + */ + @NonNull + default T namespace(@NonNull URI name) { + return namespace(ObjectUtils.notNull(name.toASCIIString())); + } + + /** + * Apply the provided name for use by this builder. + * + * @param name + * the name to use + * @return this builder + */ + @NonNull + T name(@NonNull String name); + + /** + * Apply the provided qualified name for use by this builder. + * + * @param qname + * the qualified name to use + * @return this builder + */ + @NonNull + T qname(@NonNull IEnhancedQName qname); + + /** + * Apply the provided source information for use by this builder. + * + * @param source + * the source information + * @return this builder + */ + @NonNull + T source(@NonNull ISource source); + +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModelBuilder.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModelBuilder.java new file mode 100644 index 000000000..786a358d5 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModelBuilder.java @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition; +import gov.nist.secauto.metaschema.core.model.INamedModelInstanceAbsolute; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * Represents a Metaschema module-based model builder, that supports flag + * children. + * + * @param + * the Java type of the implementation of this builder + */ +public interface IModelBuilder> extends IMetaschemaBuilder { + /** + * Use the provided flag instances for built fields. + * + * @param flags + * the flag builders to add + * @return this builder + */ + T flags(@Nullable List flags); + + /** + * Generate an instance of the implementing type using the provided parent. + * + * @param parent + * the parent assembly definition containing the instance + * @return the new instance + */ + @NonNull + INamedModelInstanceAbsolute toInstance(@NonNull IAssemblyDefinition parent); +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModuleMockFactory.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModuleMockFactory.java similarity index 50% rename from core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModuleMockFactory.java rename to core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModuleMockFactory.java index fc6a9e624..96b04063f 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IModuleMockFactory.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/IModuleMockFactory.java @@ -3,25 +3,25 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.testing; +package gov.nist.secauto.metaschema.core.testing.model; -import org.jmock.Mockery; +import gov.nist.secauto.metaschema.core.testing.model.mocking.IMockFactory; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Represents a factory used to produce Metaschema module-based definitions and + * instances. + */ public interface IModuleMockFactory extends IMockFactory { - @Override - @NonNull - Mockery getContext(); - /** * Get a new flag builder. * * @return the builder */ @NonNull - default FlagBuilder flag() { - return FlagBuilder.builder(getContext()); + default IFlagBuilder flag() { + return IFlagBuilder.builder(); } /** @@ -30,8 +30,8 @@ default FlagBuilder flag() { * @return the builder */ @NonNull - default FieldBuilder field() { - return FieldBuilder.builder(getContext()); + default IFieldBuilder field() { + return IFieldBuilder.builder(); } /** @@ -40,7 +40,7 @@ default FieldBuilder field() { * @return the builder */ @NonNull - default AssemblyBuilder assembly() { - return AssemblyBuilder.builder(getContext()); + default IAssemblyBuilder assembly() { + return IAssemblyBuilder.builder(); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/MockedModelTestSupport.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/MockedModelTestSupport.java new file mode 100644 index 000000000..678cde82e --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/MockedModelTestSupport.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model; + +import gov.nist.secauto.metaschema.core.testing.model.mocking.AbstractMockitoFactory; + +/** + * Provides the ability to generate mocked Metaschema module definitions and + * instances, along with other mocked data. + */ +public class MockedModelTestSupport + extends AbstractMockitoFactory + implements IModuleMockFactory { + /** + * Construct a new model mock factory using the default JUnit-based mocking + * context. + */ + public MockedModelTestSupport() { + // do nothing + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockFactory.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractJMockFactory.java similarity index 83% rename from core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockFactory.java rename to core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractJMockFactory.java index a496940a1..1ac4a193d 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/MockFactory.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractJMockFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.testing; +package gov.nist.secauto.metaschema.core.testing.model.mocking; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -14,7 +14,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -public class MockFactory implements IMockFactory { +public class AbstractJMockFactory implements IMockFactory { @NonNull private final Mockery context; @@ -25,11 +25,11 @@ public class MockFactory implements IMockFactory { * @param ctx * the mocking context */ - public MockFactory(@NonNull Mockery ctx) { + public AbstractJMockFactory(@NonNull Mockery ctx) { this.context = ctx; } - @Override + @NonNull public Mockery getContext() { return context; } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractMockitoFactory.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractMockitoFactory.java new file mode 100644 index 000000000..bb957adb2 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/AbstractMockitoFactory.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.testing.model.mocking; + +import static org.mockito.Mockito.withSettings; + +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.mockito.Answers; +import org.mockito.Mockito; + +import java.util.UUID; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public class AbstractMockitoFactory + implements IMockFactory { + + protected AbstractMockitoFactory() { + // allow construction by extending classes + } + + @Override + @NonNull + public T mock(@NonNull Class clazz, @Nullable String name) { + StringBuilder builder = new StringBuilder() + .append(clazz.getSimpleName()); + if (name != null) { + builder + .append('-') + .append(name); + } + builder + .append('-') + .append(UUID.randomUUID().toString()) + .toString(); + return ObjectUtils.notNull(Mockito.mock(clazz, withSettings() + .name(builder.toString()) + .defaultAnswer(Answers.CALLS_REAL_METHODS))); + } +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IMockFactory.java b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/IMockFactory.java similarity index 84% rename from core/src/test/java/gov/nist/secauto/metaschema/core/testing/IMockFactory.java rename to core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/IMockFactory.java index 42f85a713..cbb41330a 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/testing/IMockFactory.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/testing/model/mocking/IMockFactory.java @@ -3,22 +3,12 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.testing; - -import org.jmock.Mockery; +package gov.nist.secauto.metaschema.core.testing.model.mocking; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; public interface IMockFactory { - /** - * Get the mocking context. - * - * @return the mocking context - */ - @NonNull - Mockery getContext(); - /** * Create a mock for the given class. *

diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java index 7c3efe340..8fcb78df0 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/codegen/impl/AnnotationGenerator.java @@ -116,7 +116,7 @@ private static void buildConstraint(Class annotationType, AnnotationSpec.Buil annotation.addMember("level", "$T.$L", IConstraint.Level.class, constraint.getLevel()); - String target = constraint.getTarget(); + String target = constraint.getTarget().getPath(); if (!target.equals(getDefaultValue(annotationType, "target"))) { annotation.addMember("target", "$S", target); } @@ -291,7 +291,7 @@ private static void buildKeyFields( for (IKeyField key : keyFields) { AnnotationSpec.Builder keyAnnotation = AnnotationSpec.builder(KeyField.class); - String target = key.getTarget(); + String target = key.getTarget().getPath(); if (!target.equals(getDefaultValue(KeyField.class, "target"))) { keyAnnotation.addMember("target", "$S", target); } @@ -348,7 +348,7 @@ private static void applyExpectConstraints( buildConstraint(Expect.class, constraintAnnotation, constraint); - constraintAnnotation.addMember("test", "$S", constraint.getTest()); + constraintAnnotation.addMember("test", "$S", constraint.getTest().getPath()); if (constraint.getMessage() != null) { constraintAnnotation.addMember("message", "$S", constraint.getMessage()); @@ -433,7 +433,9 @@ private static void checkCardinalities( } else { warn.log(String.format( "Definition '%s' has min-occurs=%d cardinality constraint targeting '%s' that is not a model instance", - definition.getName(), constraint.getMinOccurs(), constraint.getTarget())); + definition.getName(), + constraint.getMinOccurs(), + constraint.getTarget().getPath())); } } } @@ -452,14 +454,18 @@ private static void checkMinOccurs( logBuilder.log(String.format( "Definition '%s' has min-occurs=%d cardinality constraint targeting '%s' that is redundant with a" + " targeted instance named '%s' that requires min-occurs=%d", - definition.getName(), minOccurs, constraint.getTarget(), + definition.getName(), + minOccurs, + constraint.getTarget().getPath(), modelInstance.getName(), modelInstance.getMinOccurs())); } else if (minOccurs < modelInstance.getMinOccurs()) { logBuilder.log(String.format( "Definition '%s' has min-occurs=%d cardinality constraint targeting '%s' that conflicts with a" + " targeted instance named '%s' that requires min-occurs=%d", - definition.getName(), minOccurs, constraint.getTarget(), + definition.getName(), + minOccurs, + constraint.getTarget().getPath(), modelInstance.getName(), modelInstance.getMinOccurs())); } @@ -480,14 +486,18 @@ private static void checkMaxOccurs( logBuilder.log(String.format( "Definition '%s' has max-occurs=%d cardinality constraint targeting '%s' that is redundant with a" + " targeted instance named '%s' that requires max-occurs=%d", - definition.getName(), maxOccurs, constraint.getTarget(), + definition.getName(), + maxOccurs, + constraint.getTarget().getPath(), modelInstance.getName(), modelInstance.getMaxOccurs())); } else if (maxOccurs < modelInstance.getMaxOccurs()) { logBuilder.log(String.format( "Definition '%s' has max-occurs=%d cardinality constraint targeting '%s' that conflicts with a" + " targeted instance named '%s' that requires max-occurs=%d", - definition.getName(), maxOccurs, constraint.getTarget(), + definition.getName(), + maxOccurs, + constraint.getTarget().getPath(), modelInstance.getName(), modelInstance.getMaxOccurs())); } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java index e2ee240dc..a6c5fde90 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/AllowedValues.java @@ -72,7 +72,7 @@ * @return the target metapath */ @NonNull - String target() default IConstraint.DEFAULT_TARGET_METAPATH; + String target() default "."; /** * An optional set of properties associated with these allowed values. diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/Expect.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/Expect.java index da5c93e9c..a37243c0c 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/Expect.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/Expect.java @@ -71,7 +71,7 @@ * @return the target metapath */ @NonNull - String target() default IConstraint.DEFAULT_TARGET_METAPATH; + String target() default "."; /** * An optional set of properties associated with these allowed values. diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java index 5bd11da9a..70a6b1579 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/ConstraintFactory.java @@ -9,6 +9,7 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.AbstractConfigurableMessageConstraintBuilder; import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder; @@ -56,8 +57,12 @@ static MarkupMultiline toRemarks(@NonNull String remarks) { } @NonNull - static String toMetapath(@NonNull String metapath) { - return metapath.isBlank() ? IConstraint.DEFAULT_TARGET_METAPATH : metapath; + static IMetapathExpression toMetapath( + @NonNull String metapath, + @NonNull ISource source) { + return metapath.isBlank() + ? IConstraint.DEFAULT_TARGET_METAPATH + : IMetapathExpression.lazyCompile(metapath, source.getStaticContext()); } @NonNull @@ -85,8 +90,11 @@ static String toMetapath(@NonNull String metapath) { } @NonNull - static > T applyTarget(@NonNull T builder, @NonNull String target) { - builder.target(toMetapath(target)); + static > T applyTarget( + @NonNull T builder, + @NonNull String target, + @NonNull ISource source) { + builder.target(toMetapath(target, source)); return builder; } @@ -166,7 +174,7 @@ static IAllowedValuesConstraint newAllowedValuesConstraint( builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyRemarks(builder, constraint.remarks()); @@ -186,7 +194,7 @@ static IMatchesConstraint newMatchesConstraint(Matches constraint, @NonNull ISou builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); @@ -212,10 +220,9 @@ static IMatchesConstraint newMatchesConstraint(Matches constraint, @NonNull ISou for (KeyField keyField : keyFields) { @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") // ok IKeyField field = IKeyField.of( - toMetapath(keyField.target()), + toMetapath(keyField.target(), source), toPattern(keyField.pattern()), - toRemarks(keyField.remarks()), - source); + toRemarks(keyField.remarks())); builder.keyField(field); } return builder; @@ -230,7 +237,7 @@ static IUniqueConstraint newUniqueConstraint(@NonNull IsUnique constraint, @NonN builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); @@ -249,7 +256,7 @@ static IIndexConstraint newIndexConstraint(@NonNull Index constraint, @NonNull I builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); @@ -270,7 +277,7 @@ static IIndexHasKeyConstraint newIndexHasKeyConstraint( builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); @@ -289,12 +296,12 @@ static IExpectConstraint newExpectConstraint(@NonNull Expect constraint, @NonNul builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); - builder.test(toMetapath(constraint.test())); + builder.test(toMetapath(constraint.test(), source)); return builder.build(); } @@ -314,7 +321,7 @@ static ICardinalityConstraint newCardinalityConstraint(@NonNull HasCardinality c builder .source(source) .level(constraint.level()); - applyTarget(builder, constraint.target()); + applyTarget(builder, constraint.target(), source); applyProperties(builder, constraint.properties()); applyMessage(builder, constraint.message()); applyRemarks(builder, constraint.remarks()); diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java index 6555b80ad..6e1e4404b 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/BindingConstraintLoader.java @@ -5,6 +5,7 @@ package gov.nist.secauto.metaschema.databind.model.metaschema; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.MetapathException; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.AbstractLoader; @@ -154,7 +155,7 @@ protected List parseResource(@NonNull URI resource, @NonNull Deq ObjectUtils.notNull(binding.getPrefix()), ObjectUtils.notNull(binding.getUri()))); - ISource source = ISource.externalSource(builder.build()); + ISource source = ISource.externalSource(builder.build(), false); List targetedConstraints = ObjectUtils.notNull(CollectionUtil.listOrEmpty(obj.getContexts()) .stream() @@ -235,7 +236,9 @@ private static AssemblyTargetedConstraints handleScopedAssembly( ConstraintBindingSupport.parse(constraints, obj, source); return new AssemblyTargetedConstraints( source, - ObjectUtils.requireNonNull(obj.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(obj.getTarget()), + source.getStaticContext()), constraints); } @@ -247,7 +250,9 @@ private static FieldTargetedConstraints handleScopedField( return new FieldTargetedConstraints( source, - ObjectUtils.requireNonNull(obj.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(obj.getTarget()), + source.getStaticContext()), constraints); } @@ -259,7 +264,9 @@ private static FlagTargetedConstraints handleScopedFlag( return new FlagTargetedConstraints( source, - ObjectUtils.requireNonNull(obj.getTarget()), + IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(obj.getTarget()), + source.getStaticContext()), constraints); } @@ -268,18 +275,24 @@ private Context parseContext( @Nullable Context parent, @NonNull ISource source) { - List metapaths; + List metapaths; if (parent == null) { metapaths = ObjectUtils.notNull(CollectionUtil.listOrEmpty(contextObj.getMetapaths()).stream() .map(MetaschemaMetapath::getTarget) + .map(metapath -> IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(metapath), + source.getStaticContext())) .collect(Collectors.toList())); } else { - List parentMetapaths = parent.getMetapaths().stream() + List parentMetapaths = parent.getMetapaths().stream() .collect(Collectors.toList()); metapaths = ObjectUtils.notNull(CollectionUtil.listOrEmpty(contextObj.getMetapaths()).stream() .map(MetaschemaMetapath::getTarget) .flatMap(childPath -> parentMetapaths.stream() - .map(parentPath -> parentPath + '/' + childPath)) + .map(parentPath -> parentPath.getPath() + '/' + childPath)) + .map(metapath -> IMetapathExpression.lazyCompile( + ObjectUtils.requireNonNull(metapath), + source.getStaticContext())) .collect(Collectors.toList())); } @@ -301,7 +314,7 @@ private Context parseContext( private static class Context { @NonNull - private final List metapaths; + private final List metapaths; @NonNull private final List childContexts = new LinkedList<>(); @NonNull @@ -309,13 +322,16 @@ private static class Context { public Context( @NonNull ISource source, - @NonNull List metapaths, + @NonNull List metapaths, @NonNull IModelConstrained constraints) { this.metapaths = metapaths; this.targetedConstraints = ObjectUtils.notNull(Lazy.lazy(() -> { Stream paths = getMetapaths().stream() - .map(metapath -> new MetaTargetedContraints(source, ObjectUtils.notNull(metapath), constraints)); + .map(metapath -> new MetaTargetedContraints( + source, + ObjectUtils.notNull(metapath), + constraints)); Stream childPaths = childContexts.stream() .flatMap(child -> child.getTargetedConstraints().stream()); @@ -334,7 +350,7 @@ public void addAll(@NonNull Collection childContexts) { } @NonNull - public List getMetapaths() { + public List getMetapaths() { return metapaths; } } @@ -345,7 +361,7 @@ private static class MetaTargetedContraints protected MetaTargetedContraints( @NonNull ISource source, - @NonNull String target, + @NonNull IMetapathExpression target, @NonNull IModelConstrained constraints) { super(source, target, constraints); } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java index 4568c591a..901dd582d 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/impl/ConstraintBindingSupport.java @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression; import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.model.ISource; import gov.nist.secauto.metaschema.core.model.constraint.AbstractConfigurableMessageConstraintBuilder; @@ -243,7 +244,7 @@ private static IExpectConstraint newExpect( @NonNull FlagExpect obj, @NonNull ISource source) { IExpectConstraint.Builder builder = IExpectConstraint.builder() - .test(target(ObjectUtils.requireNonNull(obj.getTest()))); + .test(metapath(ObjectUtils.requireNonNull(obj.getTest()), source)); applyConfigurableCommonValues(obj, null, source, builder); String message = obj.getMessage(); @@ -259,7 +260,7 @@ private static IExpectConstraint newExpect( @NonNull TargetedExpectConstraint obj, @NonNull ISource source) { IExpectConstraint.Builder builder = IExpectConstraint.builder() - .test(target(ObjectUtils.requireNonNull(obj.getTest()))); + .test(metapath(ObjectUtils.requireNonNull(obj.getTest()), source)); applyConfigurableCommonValues(obj, obj.getTarget(), source, builder); return builder.build(); @@ -274,10 +275,9 @@ private static IExpectConstraint newExpect( assert value != null; IKeyField keyField = IKeyField.of( - target(ObjectUtils.requireNonNull(value.getTarget())), + metapath(ObjectUtils.requireNonNull(value.getTarget()), source), pattern(value.getPattern()), - ModelSupport.remarks(value.getRemarks()), - source); + ModelSupport.remarks(value.getRemarks())); builder.keyField(keyField); } return builder; @@ -437,17 +437,21 @@ private static IUniqueConstraint newUnique( builder.remarks(ObjectUtils.notNull(remarks.getRemark())); } - builder.target(target(target)); + builder.target(metapath(target, source)); builder.level(level(constraint.getLevel())); builder.source(source); return builder; } @NonNull - private static String target(@Nullable String target) { - return target == null + private static IMetapathExpression metapath( + @Nullable String metapath, + @NonNull ISource source) { + return metapath == null ? IConstraint.DEFAULT_TARGET_METAPATH - : target; + : IMetapathExpression.lazyCompile( + metapath, + source.getStaticContext()); } @NonNull diff --git a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java index 1dc51b273..ef8796d87 100644 --- a/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java +++ b/schemagen/src/main/java/gov/nist/secauto/metaschema/schemagen/AbstractGenerationState.java @@ -93,7 +93,7 @@ protected static AllowedValueCollection getContextIndependentEnumeratedValues( closed = true; } - if (!IMetapathExpression.contextNode().getPath().equals(constraint.getTarget())) { + if (!IMetapathExpression.contextNode().getPath().equals(constraint.getTarget().getPath())) { values = CollectionUtil.emptyList(); break; }