Skip to content

Commit

Permalink
Added preceding, preceding-sibling, following, and following-sibling …
Browse files Browse the repository at this point in the history
…axes support in Metapath. Also replaced JMock with Mockito for better mocking support.
  • Loading branch information
david-waltermire committed Oct 31, 2024
1 parent b8efe51 commit 831de55
Show file tree
Hide file tree
Showing 21 changed files with 892 additions and 277 deletions.
12 changes: 12 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,23 @@
<artifactId>jmock-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.github.seregamorph</groupId>
<artifactId>hamcrest-more-matchers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bnd.util</artifactId>
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/antlr4/Metapath10.g4
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ stepexpr : postfixexpr | axisstep ;
axisstep : (reversestep | forwardstep) predicatelist ;
// [40]
forwardstep : forwardaxis nodetest | abbrevforwardstep ;
// forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_ATTRIBUTE COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON | KW_FOLLOWING_SIBLING COLONCOLON | KW_FOLLOWING COLONCOLON | KW_NAMESPACE COLONCOLON ;
forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON ;
// forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_FLAG COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON | KW_FOLLOWING_SIBLING COLONCOLON | KW_FOLLOWING COLONCOLON | KW_NAMESPACE COLONCOLON ;
forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_FLAG COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON | KW_FOLLOWING_SIBLING COLONCOLON | KW_FOLLOWING COLONCOLON ;
abbrevforwardstep : AT? nodetest ;
reversestep : reverseaxis nodetest | abbrevreversestep ;
// reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_PRECEDING_SIBLING COLONCOLON | KW_PRECEDING COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ;
reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ;
reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_PRECEDING_SIBLING COLONCOLON | KW_PRECEDING COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ;
// [45]
abbrevreversestep : DD ;
// nodetest : kindtest | nametest ;
Expand Down Expand Up @@ -120,7 +119,7 @@ unarylookup : QM keyspecifier ;
// namespacenodetest : KW_NAMESPACE_NODE OP CP ;
// pitest : KW_PROCESSING_INSTRUCTION OP (NCName | StringLiteral)? CP ;
// [90]
// attributetest : KW_ATTRIBUTE OP (attribnameorwildcard ( COMMA typename_)?)? CP ;
// attributetest : KW_FLAG OP (attribnameorwildcard ( COMMA typename_)?)? CP ;
// attribnameorwildcard : attributename | STAR ;
// schemaattributetest : KW_SCHEMA_ATTRIBUTE OP attributedeclaration CP ;
// attributedeclaration : attributename ;
Expand Down Expand Up @@ -155,7 +154,7 @@ eqname : NCName | QName | URIQualifiedName
| KW_AND
| KW_ARRAY
| KW_AS
| KW_ATTRIBUTE
| KW_FLAG
| KW_CAST
| KW_CASTABLE
| KW_CHILD
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/antlr4/Metapath10Lexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ KW_ANCESTOR_OR_SELF : 'ancestor-or-self';
KW_AND : 'and';
KW_ARRAY : 'array';
KW_AS : 'as';
KW_ATTRIBUTE : 'attribute';
KW_FLAG : 'flag';
KW_CAST : 'cast';
KW_CASTABLE : 'castable';
KW_CHILD : 'child';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ default Iterator<ITEM> iterator() {
* {@code true}
*/
static <T extends IItem> T getFirstItem(@NonNull ISequence<T> items, boolean requireSingleton) {
return getFirstItem(items.stream(), requireSingleton);
return getFirstItem(items.safeStream(), requireSingleton);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected Metapath10ParserBase(TokenStream input) {
*/
protected boolean isFuncCall() {
return !(getInputStream().LA(1) == Metapath10.KW_ARRAY
|| getInputStream().LA(1) == Metapath10.KW_ATTRIBUTE
|| getInputStream().LA(1) == Metapath10.KW_FLAG
|| getInputStream().LA(1) == Metapath10.KW_COMMENT
|| getInputStream().LA(1) == Metapath10.KW_DOCUMENT_NODE
|| getInputStream().LA(1) == Metapath10.KW_ELEMENT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public class ArraySequenceConstructor implements IExpression {
* Construct a new array constructor expression that uses the provided
* expression to initialize the array.
*
* @param expr
* @param expression
* the expression used to produce the array members
*/
public ArraySequenceConstructor(@Nullable IExpression expr) {
this.expr = expr;
public ArraySequenceConstructor(@Nullable IExpression expression) {
this.expr = expression;
}

@SuppressWarnings("rawtypes")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,15 @@ protected IExpression handleForwardstep(ForwardstepContext ctx) {
case Metapath10Lexer.KW_DESCENDANT_OR_SELF:
axis = Axis.DESCENDANT_OR_SELF;
break;
case Metapath10Lexer.KW_FLAG:
axis = Axis.FLAG;
break;
case Metapath10Lexer.KW_FOLLOWING_SIBLING:
axis = Axis.FOLLOWING_SIBLING;
break;
case Metapath10Lexer.KW_FOLLOWING:
axis = Axis.FOLLOWING;
break;
default:
throw new UnsupportedOperationException(token.getText());
}
Expand Down Expand Up @@ -685,6 +694,12 @@ protected IExpression handleReversestep(ReversestepContext ctx) {
case Metapath10Lexer.KW_ANCESTOR_OR_SELF:
axis = Axis.ANCESTOR_OR_SELF;
break;
case Metapath10Lexer.KW_PRECEDING_SIBLING:
axis = Axis.PRECEDING_SIBLING;
break;
case Metapath10Lexer.KW_PRECEDING:
axis = Axis.PRECEDING;
break;
default:
throw new UnsupportedOperationException(token.getText());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@ public class Except
extends AbstractFilterExpression {

/**
* Construct a new Metapath except expression CST node.
* Construct a except filter expression, which removes the items resulting from
* the filter expression from the items expression.
*
* @param left
* @param itemsExpression
* an expression indicating the items to filter
* @param right
* @param filterExpression
* an expression indicating the items to omit
*/
public Except(@NonNull IExpression left, @NonNull IExpression right) {
super(left, right);
public Except(@NonNull IExpression itemsExpression, @NonNull IExpression filterExpression) {
super(itemsExpression, filterExpression);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@
public enum Axis implements IExpression {
SELF(Stream::of),
PARENT(focus -> Stream.ofNullable(focus.getParentNodeItem())),
FLAG(INodeItem::flags),
ANCESTOR(INodeItem::ancestor),
ANCESTOR_OR_SELF(INodeItem::ancestorOrSelf),
CHILDREN(INodeItem::modelItems),
DESCENDANT(INodeItem::descendant),
DESCENDANT_OR_SELF(INodeItem::descendantOrSelf);
DESCENDANT_OR_SELF(INodeItem::descendantOrSelf),
FOLLOWING_SIBLING(INodeItem::followingSibling),
PRECEDING_SIBLING(INodeItem::precedingSibling),
FOLLOWING(INodeItem::following),
PRECEDING(INodeItem::preceding);

@NonNull
private final Function<INodeItem, Stream<? extends INodeItem>> action;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ public <RESULT, CONTEXT> RESULT accept(IExpressionVisitor<RESULT, CONTEXT> visit

@Override
public ISequence<?> accept(DynamicContext dynamicContext, ISequence<?> focus) {

ISequence<? extends INodeItem> axisResult = getAxis().accept(dynamicContext, focus);
return getStep().accept(dynamicContext, axisResult);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,23 @@ public StreamSequence(@NonNull Stream<ITEM> stream) {
this.stream = stream;
}

@Override
public Object[] toArray() {
return getValue().toArray();
}

@Override
public <T> T[] toArray(T[] a) {
return getValue().toArray(a);
}

@Override
public List<ITEM> getValue() {
instanceLock.lock();
try {
if (list == null) {
list = stream().collect(Collectors.toUnmodifiableList());
list = stream.collect(Collectors.toUnmodifiableList());
stream = null;
}
assert list != null;
return list;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,4 @@ default INodeItem getParentNodeItem() {
// there is no parent
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@

import gov.nist.secauto.metaschema.core.model.IModelDefinition;
import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.stream.Stream;

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

public interface IModelNodeItem<D extends IModelDefinition, I extends INamedModelInstance>
extends IDefinitionNodeItem<D, I> {

/**
* Retrieve the relative position of this node relative to sibling nodes.
* <p>
* A singleton item in a sequence will have a position value of {@code 1}.
* <p>
* The value {@code 1} is used as the starting value to align with the XPath
* specification.
*
* @return a positive integer value designating this instance's position within
* a collection
*/
@Override
int getPosition();

/**
Expand All @@ -30,4 +25,46 @@ public interface IModelNodeItem<D extends IModelDefinition, I extends INamedMode

@Override
IAssemblyNodeItem getParentContentNodeItem();

@Override
@NonNull
default Stream<? extends IModelNodeItem<?, ?>> descendantOrSelf() {
return ObjectUtils.notNull(Stream.concat(Stream.of(this), descendant()));
}

@SuppressWarnings("PMD.CompareObjectsWithEquals")
@Override
@NonNull
default Stream<? extends IModelNodeItem<?, ?>> followingSibling() {
IModelNodeItem<?, ?> parent = getParentContentNodeItem();
return ObjectUtils.notNull(parent == null
? Stream.empty()
: parent.modelItems()
// need to use != vs !Object.equals to ensure we are matching the same object
.dropWhile(item -> this != item)
.skip(1));
}

@SuppressWarnings("PMD.CompareObjectsWithEquals")
@Override
@NonNull
default Stream<? extends IModelNodeItem<?, ?>> precedingSibling() {
IModelNodeItem<?, ?> parent = getParentContentNodeItem();
return ObjectUtils.notNull(parent == null
? Stream.empty()
// need to use != vs !Object.equals to ensure we are matching the same object
: parent.modelItems().takeWhile(item -> this != item));
}

@Override
default Stream<? extends IModelNodeItem<?, ?>> following() {
return followingSibling()
.flatMap(IModelNodeItem::descendantOrSelf);
}

@Override
default Stream<? extends IModelNodeItem<?, ?>> preceding() {
return precedingSibling()
.flatMap(IModelNodeItem::descendantOrSelf);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@
* Represents a queryable Metapath model node.
*/
public interface INodeItem extends IItem, IPathSegment, INodeItemVisitable {
/**
* Retrieve the relative position of this node relative to sibling nodes.
* <p>
* A singleton item in a sequence will have a position value of {@code 1}.
* <p>
* The value {@code 1} is used as the starting value to align with the XPath
* specification.
*
* @return a positive integer value designating this instance's position within
* a collection
*/
default int getPosition() {
// only model node items have positions other than 1
return 1;
}

/**
* Generate a path for this node in the directed node graph, using the provided
Expand Down Expand Up @@ -159,7 +174,7 @@ static Stream<? extends INodeItem> ancestorsOf(@NonNull INodeItem item) {
* @return a stream of descendant node items
*/
@NonNull
default Stream<? extends INodeItem> descendant() {
default Stream<? extends IModelNodeItem<?, ?>> descendant() {
return decendantsOf(this);
}

Expand All @@ -174,8 +189,8 @@ default Stream<? extends INodeItem> descendant() {
* @return a stream of descendant node items
*/
@NonNull
static Stream<? extends INodeItem> decendantsOf(@NonNull INodeItem item) {
Stream<? extends INodeItem> children = item.modelItems();
static Stream<? extends IModelNodeItem<?, ?>> decendantsOf(@NonNull INodeItem item) {
Stream<? extends IModelNodeItem<?, ?>> children = item.modelItems();

return ObjectUtils.notNull(children.flatMap(child -> {
assert child != null;
Expand All @@ -195,6 +210,48 @@ default Stream<? extends INodeItem> descendantOrSelf() {
return ObjectUtils.notNull(Stream.concat(Stream.of(this), descendant()));
}

/**
* Get the children of this node's parent that occur after this node in a
* depth-first order.
*
* @return a stream of nodes
*/
@NonNull
default Stream<? extends IModelNodeItem<?, ?>> followingSibling() {
return ObjectUtils.notNull(Stream.empty());
}

/**
* Get the children of this node's parent, and their descendants, that occur
* before this node in a depth-first order.
*
* @return a stream of nodes
*/
@NonNull
default Stream<? extends IModelNodeItem<?, ?>> precedingSibling() {
return ObjectUtils.notNull(Stream.empty());
}

/**
* Get the children of this node's parent, and their descendants, that occur
* before this node in a depth-first order.
*
* @return a stream of nodes
*/
default Stream<? extends IModelNodeItem<?, ?>> following() {
return ObjectUtils.notNull(Stream.empty());
}

/**
* Get the children of this node's parent, and their descendants, that occur
* after this node in a depth-first order.
*
* @return a stream of nodes
*/
default Stream<? extends IModelNodeItem<?, ?>> preceding() {
return ObjectUtils.notNull(Stream.empty());
}

/**
* Get the flags and value data associated this node. The resulting collection
* is expected to be ordered, with the results in document order.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import edu.umd.cs.findbugs.annotations.NonNull;

public class ExpressionTestBase {
@NonNull
protected static final URI NS_URI = ObjectUtils.notNull(URI.create("http://example.com/ns"));
@NonNull
protected static final String NS = ObjectUtils.notNull(NS_URI.toASCIIString());

@SuppressWarnings("exports")
@NonNull
@RegisterExtension
Expand All @@ -47,6 +52,7 @@ protected static DynamicContext newDynamicContext() {

return new DynamicContext(StaticContext.builder()
.baseUri(baseUri)
.defaultModelNamespace(NS_URI)
.build());
}

Expand Down
Loading

0 comments on commit 831de55

Please sign in to comment.