> arguments,
+ @NonNull DynamicContext dynamicContext,
+ IItem focus) {
+ INodeItem arg = FunctionUtils.asTypeOrNull(ObjectUtils.requireNonNull(arguments.get(0)).getFirstItem(true));
+
+ return ISequence.of(
+ IStringItem.valueOf(arg == null ? "" : fnLocalName(arg, dynamicContext.getStaticContext())));
+ }
+
+ /**
+ * Get the name of the provided node item.
+ *
+ * Based on the XPath 3.1 fn:local-name
+ * function.
+ *
+ * @param arg
+ * the node item to get the name for
+ * @param staticContext
+ * the static context used to resolve the namespace prefix
+ * @return the name of the node if it has one, or an empty string otherwise
+ */
+ @NonNull
+ public static String fnLocalName(@NonNull INodeItem arg, @NonNull StaticContext staticContext) {
+ return arg instanceof IDefinitionNodeItem
+ ? ((IDefinitionNodeItem, ?>) arg).getQName().getLocalName()
+ : "";
+ }
+
+ private FnLocalName() {
+ // disable construction
+ }
+}
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
new file mode 100644
index 000000000..6a0647477
--- /dev/null
+++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnLocalNameTest.java
@@ -0,0 +1,117 @@
+/*
+ * SPDX-FileCopyrightText: none
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+package gov.nist.secauto.metaschema.core.metapath.function.library;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
+import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException;
+import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase;
+import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
+import gov.nist.secauto.metaschema.core.metapath.MetapathException;
+import gov.nist.secauto.metaschema.core.metapath.function.library.impl.MockedDocumentGenerator;
+import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
+import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
+import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException;
+import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
+class FnLocalNameTest
+ extends ExpressionTestBase {
+
+ private static Stream provideValues() { // NOPMD - false positive
+ return Stream.of(
+ Arguments.of(
+ null,
+ "local-name()"),
+ Arguments.of(
+ null,
+ "local-name(.)"),
+ Arguments.of(
+ MockedDocumentGenerator.ROOT_QNAME,
+ "local-name(/root)"),
+ Arguments.of(
+ MockedDocumentGenerator.ASSEMBLY_QNAME,
+ "local-name(/root/assembly)"),
+ Arguments.of(
+ MockedDocumentGenerator.ASSEMBLY_FLAG_QNAME,
+ "local-name(/root/assembly/@assembly-flag)"),
+ Arguments.of(
+ MockedDocumentGenerator.FIELD_QNAME,
+ "local-name(/root/field)"),
+ Arguments.of(
+ MockedDocumentGenerator.FIELD_FLAG_QNAME,
+ "local-name(/root/field/@field-flag)"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideValues")
+ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) {
+ DynamicContext dynamicContext = newDynamicContext();
+
+ IStringItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext())
+ .evaluateAs(
+ MockedDocumentGenerator.generateDocumentNodeItem(getContext()),
+ IMetapathExpression.ResultType.ITEM,
+ dynamicContext);
+ assertNotNull(result);
+ assertEquals(
+ expected == null
+ ? ""
+ : expected.getLocalName(),
+ result.asString());
+ }
+
+ @Test
+ void testContextAbsent() {
+ DynamicContext dynamicContext = newDynamicContext();
+
+ MetapathException ex = assertThrows(MetapathException.class, () -> {
+ IMetapathExpression.compile("local-name()", dynamicContext.getStaticContext())
+ .evaluateAs(null, IMetapathExpression.ResultType.ITEM, dynamicContext);
+ });
+ Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null;
+
+ assertAll(
+ () -> assertEquals(DynamicMetapathException.class, cause == null
+ ? null
+ : cause.getClass()),
+ () -> assertEquals(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, cause instanceof DynamicMetapathException
+ ? ((DynamicMetapathException) cause).getCode()
+ : null));
+ }
+
+ @Test
+ void testNotANode() {
+ DynamicContext dynamicContext = newDynamicContext();
+
+ MetapathException ex = assertThrows(MetapathException.class, () -> {
+ IMetapathExpression.compile("local-name()", dynamicContext.getStaticContext())
+ .evaluateAs(IStringItem.valueOf("test"), IMetapathExpression.ResultType.ITEM, dynamicContext);
+ });
+ Throwable cause = ex.getCause() != null ? ex.getCause().getCause() : null;
+
+ assertAll(
+ () -> assertEquals(InvalidTypeMetapathException.class, cause == null
+ ? null
+ : cause.getClass()),
+ () -> assertEquals(TypeMetapathException.INVALID_TYPE_ERROR, cause instanceof TypeMetapathException
+ ? ((TypeMetapathException) cause).getCode()
+ : null));
+ }
+}
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 f9ccd1aa1..5b9a376d9 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
@@ -10,33 +10,22 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
-import gov.nist.secauto.metaschema.core.mdm.IDMAssemblyNodeItem;
-import gov.nist.secauto.metaschema.core.mdm.IDMDocumentNodeItem;
-import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem;
-import gov.nist.secauto.metaschema.core.mdm.IDMRootAssemblyNodeItem;
import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException;
import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase;
import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
import gov.nist.secauto.metaschema.core.metapath.MetapathException;
+import gov.nist.secauto.metaschema.core.metapath.function.library.impl.MockedDocumentGenerator;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
-import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem;
import gov.nist.secauto.metaschema.core.metapath.type.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.metapath.type.TypeMetapathException;
-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.testing.MockedModelTestSupport;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import java.net.URI;
import java.util.stream.Stream;
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -44,41 +33,6 @@
class FnNameTest
extends ExpressionTestBase {
- @NonNull
- private static final IEnhancedQName ROOT_QNAME = IEnhancedQName.of(NS, "root");
- @NonNull
- private static final IEnhancedQName ASSEMBLY_QNAME = IEnhancedQName.of(NS, "assembly");
- @NonNull
- private static final IEnhancedQName FIELD_QNAME = IEnhancedQName.of(NS, "field");
- @NonNull
- private static final IEnhancedQName ASSEMBLY_FLAG_QNAME = IEnhancedQName.of("assembly-flag");
- @NonNull
- private static final IEnhancedQName FIELD_FLAG_QNAME = IEnhancedQName.of("field-flag");
-
- private IDocumentNodeItem newDocumentNodeItem() {
- MockedModelTestSupport mocking = new MockedModelTestSupport(getContext());
- IResourceLocation resourceLocation = mocking.mock(IResourceLocation.class);
-
- 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());
-
- IDMDocumentNodeItem document = IDMDocumentNodeItem.newInstance(
- URI.create("https://example.com/resource"),
- resourceLocation,
- rootDefinition,
- resourceLocation);
- 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"));
-
- return document;
- }
-
private static Stream provideValues() { // NOPMD - false positive
return Stream.of(
Arguments.of(
@@ -88,19 +42,19 @@ private static Stream provideValues() { // NOPMD - false positive
null,
"name(.)"),
Arguments.of(
- ROOT_QNAME,
+ MockedDocumentGenerator.ROOT_QNAME,
"name(/root)"),
Arguments.of(
- ASSEMBLY_QNAME,
+ MockedDocumentGenerator.ASSEMBLY_QNAME,
"name(/root/assembly)"),
Arguments.of(
- ASSEMBLY_FLAG_QNAME,
+ MockedDocumentGenerator.ASSEMBLY_FLAG_QNAME,
"name(/root/assembly/@assembly-flag)"),
Arguments.of(
- FIELD_QNAME,
+ MockedDocumentGenerator.FIELD_QNAME,
"name(/root/field)"),
Arguments.of(
- FIELD_FLAG_QNAME,
+ MockedDocumentGenerator.FIELD_FLAG_QNAME,
"name(/root/field/@field-flag)"));
}
@@ -110,7 +64,10 @@ void test(@Nullable IEnhancedQName expected, @NonNull String metapath) {
DynamicContext dynamicContext = newDynamicContext();
IStringItem result = IMetapathExpression.compile(metapath, dynamicContext.getStaticContext())
- .evaluateAs(newDocumentNodeItem(), IMetapathExpression.ResultType.ITEM, dynamicContext);
+ .evaluateAs(
+ MockedDocumentGenerator.generateDocumentNodeItem(getContext()),
+ IMetapathExpression.ResultType.ITEM,
+ dynamicContext);
assertNotNull(result);
assertEquals(
expected == null
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
new file mode 100644
index 000000000..065a22e1a
--- /dev/null
+++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/impl/MockedDocumentGenerator.java
@@ -0,0 +1,65 @@
+
+package gov.nist.secauto.metaschema.core.metapath.function.library.impl;
+
+import gov.nist.secauto.metaschema.core.mdm.IDMAssemblyNodeItem;
+import gov.nist.secauto.metaschema.core.mdm.IDMDocumentNodeItem;
+import gov.nist.secauto.metaschema.core.mdm.IDMFieldNodeItem;
+import gov.nist.secauto.metaschema.core.mdm.IDMRootAssemblyNodeItem;
+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.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.testing.MockedModelTestSupport;
+
+import org.jmock.Mockery;
+
+import java.net.URI;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+public class MockedDocumentGenerator {
+ @NonNull
+ public static final String NS = "http://example.com/ns";
+ @NonNull
+ public static final IEnhancedQName ROOT_QNAME = IEnhancedQName.of(NS, "root");
+ @NonNull
+ public static final IEnhancedQName ASSEMBLY_QNAME = IEnhancedQName.of(NS, "assembly");
+ @NonNull
+ public static final IEnhancedQName FIELD_QNAME = IEnhancedQName.of(NS, "field");
+ @NonNull
+ public static final IEnhancedQName ASSEMBLY_FLAG_QNAME = IEnhancedQName.of("assembly-flag");
+ @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);
+
+ 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());
+
+ IDMDocumentNodeItem document = IDMDocumentNodeItem.newInstance(
+ URI.create("https://example.com/resource"),
+ resourceLocation,
+ rootDefinition,
+ resourceLocation);
+ 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"));
+
+ return document;
+ }
+
+ private MockedDocumentGenerator() {
+ // disable construction
+ }
+
+}
diff --git a/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/MetaschemaModuleMetaschemaTest.java b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/MetaschemaModuleMetaschemaTest.java
index 490b11e26..3711c1be8 100644
--- a/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/MetaschemaModuleMetaschemaTest.java
+++ b/databind/src/test/java/gov/nist/secauto/metaschema/databind/io/MetaschemaModuleMetaschemaTest.java
@@ -98,7 +98,7 @@ void testOscalBindingModuleLoader() throws MetaschemaException, IOException {
IBindingModuleLoader loader = newBindingContext().newModuleLoader();
loader.allowEntityResolution();
IBindingMetaschemaModule module = loader.load(ObjectUtils.notNull(URI.create(
- "https://raw.githubusercontent.com/usnistgov/OSCAL/refs/tags/v1.1.2/src/metaschema/oscal_complete_metaschema.xml")));
+ "https://raw.githubusercontent.com/usnistgov/OSCAL/refs/tags/v1.1.3/src/metaschema/oscal_complete_metaschema.xml")));
assertNotNull(module);
}
@@ -108,7 +108,7 @@ void testOscalXmlModuleLoader() throws MetaschemaException, IOException {
// loader.allowEntityResolution();
IXmlMetaschemaModule module = loader.load(ObjectUtils.notNull(URI.create(
- "https://raw.githubusercontent.com/usnistgov/OSCAL/refs/tags/v1.1.2/src/metaschema/oscal_complete_metaschema.xml")));
+ "https://raw.githubusercontent.com/usnistgov/OSCAL/refs/tags/v1.1.3/src/metaschema/oscal_complete_metaschema.xml")));
assertNotNull(module);
}
}