Skip to content

Commit

Permalink
Added support for the array:head and array:tail Metapath functions. R…
Browse files Browse the repository at this point in the history
…efactored array construction and handling to allow sequences to be members of an array.
  • Loading branch information
david-waltermire committed May 26, 2024
1 parent 8ec41fb commit 5e0b678
Show file tree
Hide file tree
Showing 30 changed files with 501 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Portions of this software was developed by employees of the National Institute
* of Standards and Technology (NIST), an agency of the Federal Government and is
* being made available as a public service. Pursuant to title 17 United States
* Code Section 105, works of NIST employees are not subject to copyright
* protection in the United States. This software may be subject to foreign
* copyright. Permission in the United States and in foreign countries, to the
* extent that NIST may hold copyright, to use, copy, modify, create derivative
* works, and distribute this software and its documentation without fee is hereby
* granted on a non-exclusive basis, provided that this notice and disclaimer
* of warranty appears in all copies.
*
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
*/

package gov.nist.secauto.metaschema.core.metapath;

import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.stream.Stream;

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

public interface ICollectionValue {
// TODO: rename to toSequence and resolve conflicting methods?
@NonNull
ISequence<?> asSequence();

@NonNull
static Stream<? extends IItem> normalizeAsItems(@NonNull ICollectionValue value) {
return value instanceof IItem ? ObjectUtils.notNull(Stream.of((IItem) value)) : value.asSequence().stream();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
* the Java type of the items in a sequence
*/
@SuppressWarnings("PMD.ShortMethodName")
public interface ISequence<ITEM extends IItem> extends List<ITEM>, IStringValued {
public interface ISequence<ITEM extends IItem> extends List<ITEM>, IStringValued, ICollectionValue {
/**
* Get an empty sequence.
*
Expand Down Expand Up @@ -154,17 +154,17 @@ default ITEM getFirstItem(boolean requireSingleton) {
}

@NonNull
default IItem toArrayMember() {
IItem retval;
default ICollectionValue toArrayMember() {
ICollectionValue retval;
switch (size()) {
case 0:
retval = IArrayItem.empty();
retval = ISequence.empty();
break;
case 1:
retval = stream().findFirst().get();
retval = ObjectUtils.notNull(stream().findFirst().get());
break;
default:
retval = IArrayItem.ofCollection(this);
retval = this;
}
return retval;
}
Expand All @@ -179,17 +179,6 @@ default Stream<ITEM> safeStream() {
return ObjectUtils.notNull(getValue().stream());
}

/**
* This optional operation ensures that a list is used to back this sequence.
* <p>
* If a stream is currently backing this sequence, the stream will be collected
* into a list. This ensures the sequence can be visited multiple times.
*
* @return the resulting sequence
*/
@NonNull
ISequence<ITEM> collect();

/**
* A {@link Collector} implementation to generates a sequence from a stream of
* Metapath items.
Expand Down Expand Up @@ -232,6 +221,11 @@ public Set<Characteristics> characteristics() {
};
}

@Override
default ISequence<ITEM> asSequence() {
return this;
}

/**
* Apply the provided {@code mapFunction} to each item in the sequence.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException;
import gov.nist.secauto.metaschema.core.metapath.function.library.FnData;
Expand Down Expand Up @@ -55,11 +56,11 @@ public IKeySpecifier getKeySpecifier() {

protected interface IKeySpecifier {

default Stream<? extends IItem> lookup(
default Stream<? extends ICollectionValue> lookup(
@NonNull IItem item,
@NonNull DynamicContext dynamicContext,
@NonNull ISequence<?> focus) {
Stream<? extends IItem> result;
Stream<? extends ICollectionValue> result;
if (item instanceof IArrayItem) {
result = lookupInArray((IArrayItem<?>) item, dynamicContext, focus);
} else {
Expand All @@ -69,7 +70,7 @@ default Stream<? extends IItem> lookup(
return result;
}

Stream<? extends IItem> lookupInArray(
Stream<? extends ICollectionValue> lookupInArray(
@NonNull IArrayItem<?> item,
@NonNull DynamicContext dynamicContext,
@NonNull ISequence<?> focus);
Expand Down Expand Up @@ -105,7 +106,7 @@ public IntegerLiteralKeySpecifier(IIntegerItem literal) {
}

@Override
public Stream<? extends IItem> lookupInArray(
public Stream<? extends ICollectionValue> lookupInArray(
IArrayItem<?> item,
DynamicContext dynamicContext,
ISequence<?> focus) {
Expand All @@ -125,7 +126,7 @@ public Stream<? extends IItem> lookupInArray(
protected static class WildcardKeySpecifier implements IKeySpecifier {

@Override
public Stream<? extends IItem> lookupInArray(
public Stream<? extends ICollectionValue> lookupInArray(
IArrayItem<?> item,
DynamicContext dynamicContext,
ISequence<?> focus) {
Expand All @@ -146,7 +147,7 @@ public IExpression getKeyExpression() {
}

@Override
public Stream<? extends IItem> lookupInArray(
public Stream<? extends ICollectionValue> lookupInArray(
IArrayItem<?> item,
DynamicContext dynamicContext,
ISequence<?> focus) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;

Expand Down Expand Up @@ -68,7 +69,8 @@ public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequenc
IKeySpecifier specifier = getKeySpecifier();

return ISequence.of(base.stream()
.flatMap(item -> specifier.lookup(item, dynamicContext, focus)));
.flatMap(item -> specifier.lookup(item, dynamicContext, focus))
.flatMap(ICollectionValue::normalizeAsItems));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gov.nist.secauto.metaschema.core.metapath.cst;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;

Expand All @@ -52,7 +53,8 @@ public ISequence<? extends IItem> accept(DynamicContext dynamicContext, ISequenc
IKeySpecifier specifier = getKeySpecifier();

return ISequence.of(focus.stream()
.flatMap(item -> specifier.lookup(item, dynamicContext, focus)));
.flatMap(item -> specifier.lookup(item, dynamicContext, focus))
.flatMap(ICollectionValue::normalizeAsItems));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visit

@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {
focus.collect();
// ensure the sequence is backed by a list
focus.getValue();

// now process the union
@NonNull Stream<? extends IItem> retval = ObjectUtils.notNull(getChildren().stream()
.flatMap(child -> {
ISequence<?> result = child.accept(dynamicContext, focus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ protected Stream<? extends INodeItem> searchExpression(
@NonNull IExpression expression,
@NonNull DynamicContext dynamicContext,
@NonNull ISequence<?> outerFocus) {

outerFocus.collect();
// ensure the sequence is backed by a list
outerFocus.getValue();

// check the current focus
@SuppressWarnings("unchecked") Stream<? extends INodeItem> nodeMatches
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gov.nist.secauto.metaschema.core.metapath.function.library;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
Expand Down Expand Up @@ -63,13 +64,13 @@ public class ArrayAppend {

@SuppressWarnings("unused")
@NonNull
private static <T extends IItem> ISequence<IArrayItem<T>> execute(@NonNull IFunction function,
private static <T extends ICollectionValue> ISequence<IArrayItem<T>> execute(@NonNull IFunction function,
@NonNull List<ISequence<?>> arguments,
@NonNull DynamicContext dynamicContext,
IItem focus) {
IArrayItem<T> array = FunctionUtils.asType(ObjectUtils.requireNonNull(
arguments.get(0).getFirstItem(true)));
T appendage = FunctionUtils.asType(arguments.get(1).toArrayMember());
@SuppressWarnings("unchecked") T appendage = (T) arguments.get(1).toArrayMember();

return ISequence.of(append(array, appendage));
}
Expand All @@ -87,7 +88,7 @@ private static <T extends IItem> ISequence<IArrayItem<T>> execute(@NonNull IFunc
* @return a new array containing the modification
*/
@NonNull
public static <T extends IItem> IArrayItem<T> append(
public static <T extends ICollectionValue> IArrayItem<T> append(
@NonNull IArrayItem<T> array,
@NonNull T appendage) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
package gov.nist.secauto.metaschema.core.metapath.function.library;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
Expand Down Expand Up @@ -71,7 +72,7 @@ private static ISequence<?> execute(@NonNull IFunction function,
IArrayItem<?> array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true)));
IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true)));

return ISequence.of(get(array, position));
return get(array, position).asSequence();
}

/**
Expand All @@ -89,7 +90,7 @@ private static ISequence<?> execute(@NonNull IFunction function,
* if the position is not in the range of 1 to array:size
*/
@NonNull
public static <T extends IItem> T get(
public static <T extends ICollectionValue> T get(
@NonNull List<T> target,
@NonNull IIntegerItem positionItem) {
int position = positionItem.asInteger().intValue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Portions of this software was developed by employees of the National Institute
* of Standards and Technology (NIST), an agency of the Federal Government and is
* being made available as a public service. Pursuant to title 17 United States
* Code Section 105, works of NIST employees are not subject to copyright
* protection in the United States. This software may be subject to foreign
* copyright. Permission in the United States and in foreign countries, to the
* extent that NIST may hold copyright, to use, copy, modify, create derivative
* works, and distribute this software and its documentation without fee is hereby
* granted on a non-exclusive basis, provided that this notice and disclaimer
* of warranty appears in all copies.
*
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
*/

package gov.nist.secauto.metaschema.core.metapath.function.library;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ICollectionValue;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.List;

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

public class ArrayHead {
@NonNull
static final IFunction SIGNATURE = IFunction.builder()
.name("head")
.namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY)
.argument(IArgument.builder()
.name("array")
.type(IArrayItem.class)
.one()
.build())
.returnType(IItem.class)
.returnZeroOrOne()
.functionHandler(ArrayHead::execute)
.build();

@SuppressWarnings("unused")
@NonNull
private static ISequence<?> execute(@NonNull IFunction function,
@NonNull List<ISequence<?>> arguments,
@NonNull DynamicContext dynamicContext,
IItem focus) {
IArrayItem<?> array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true)));

ICollectionValue result = head(array);
return result == null ? ISequence.empty() : result.asSequence();
}

/**
* An implementation of XPath 3.1 <a href=
* "https://www.w3.org/TR/xpath-functions-31/#func-array-head">array:head</a>.
*
* @param <T>
* the type of items in the given Metapath array
* @param array
* the array to get the head item from
* @return the head item
*/
@Nullable
public static <T extends ICollectionValue> T head(@NonNull IArrayItem<T> array) {
return array.isEmpty() ? null : array.get(0);
}
}
Loading

0 comments on commit 5e0b678

Please sign in to comment.