From fd72d3272fcb4f93dc794e03d290ccccedb2bed8 Mon Sep 17 00:00:00 2001 From: David Waltermire Date: Fri, 31 May 2024 11:50:18 -0400 Subject: [PATCH] Added support for Metapath maps and map lookups using function, unary, and postfix methods. --- core/src/main/antlr4/Metapath10.g4 | 17 +- core/src/main/antlr4/Metapath10Lexer.g4 | 298 +++---- .../core/metapath/ICollectionValue.java | 4 +- .../{IStringValued.java => IPrintable.java} | 2 +- .../metaschema/core/metapath/ISequence.java | 4 +- .../core/metapath/MetapathConstants.java | 5 + .../core/metapath/MetapathExpression.java | 9 + .../core/metapath/StaticContext.java | 3 + .../metapath/antlr/AbstractAstVisitor.java | 214 +++-- .../metapath/cst/AbstractCSTVisitorBase.java | 14 +- .../cst/AbstractExpressionVisitor.java | 14 +- .../core/metapath/cst/AbstractLookup.java | 69 +- ...nce.java => ArraySequenceConstructor.java} | 4 +- ...quare.java => ArraySquareConstructor.java} | 7 +- .../core/metapath/cst/BuildCSTVisitor.java | 56 +- .../core/metapath/cst/CSTPrinter.java | 13 +- .../metapath/cst/FunctionCallAccessor.java | 24 +- .../core/metapath/cst/IExpressionVisitor.java | 26 +- .../core/metapath/cst/MapConstructor.java | 114 +++ .../function/library/ArrayAppend.java | 2 +- .../metapath/function/library/ArrayGet.java | 7 +- .../function/library/ArrayInsertBefore.java | 2 +- .../metapath/function/library/ArrayPut.java | 2 +- .../library/DefaultFunctionLibrary.java | 3 + .../metapath/function/library/MapGet.java | 100 +++ .../core/metapath/impl/AbstractMapItem.java | 105 +++ .../metapath/impl/AbstractStringMapKey.java | 45 ++ .../metapath/impl/ImmutableCollections.java | 92 ++- .../core/metapath/impl/MapItemN.java | 57 ++ .../item/atomic/AbstractDateItem.java | 7 +- .../item/atomic/AbstractDateTimeItem.java | 7 +- .../item/atomic/AbstractDecimalItem.java | 66 ++ .../item/atomic/AbstractIntegerItem.java | 2 +- .../item/atomic/AbstractStringItem.java | 17 + .../item/atomic/AbstractTemporalItem.java | 74 ++ .../atomic/AbstractUntypedAtomicItem.java | 55 ++ .../metapath/item/atomic/AbstractUriItem.java | 17 + .../item/atomic/Base64BinaryItemImpl.java | 25 + .../metapath/item/atomic/BooleanItemImpl.java | 25 + .../item/atomic/DayTimeDurationItemImpl.java | 25 + .../metapath/item/atomic/DecimalItemImpl.java | 3 +- .../metapath/item/atomic/IAnyAtomicItem.java | 8 +- .../metapath/item/atomic/IIPAddressItem.java | 3 - .../item/atomic/IPv4AddressItemImpl.java | 2 +- .../item/atomic/IPv6AddressItemImpl.java | 2 +- .../metapath/item/atomic/ITemporalItem.java | 31 + .../item/atomic/MarkupLineItemImpl.java | 2 +- .../item/atomic/MarkupMultiLineItemImpl.java | 2 +- .../metapath/item/atomic/UuidItemImpl.java | 25 + .../atomic/YearMonthDurationItemImpl.java | 25 + .../metapath/item/function/IArrayItem.java | 10 +- .../core/metapath/item/function/IMapItem.java | 728 ++++++++++++++++++ .../core/metapath/item/function/IMapKey.java | 43 ++ core/src/main/java/module-info.java | 29 +- .../metapath/function/library/MapGetTest.java | 66 ++ .../item/function/IArrayItemTest.java | 47 -- .../metapath/item/function/LookupTest.java | 102 +++ .../annotations/NullJavaTypeAdapter.java | 6 + 58 files changed, 2379 insertions(+), 387 deletions(-) rename core/src/main/java/gov/nist/secauto/metaschema/core/metapath/{IStringValued.java => IPrintable.java} (98%) rename core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/{ArraySequence.java => ArraySequenceConstructor.java} (95%) rename core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/{ArraySquare.java => ArraySquareConstructor.java} (93%) create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/MapConstructor.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGet.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java create mode 100644 core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapKey.java create mode 100644 core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGetTest.java create mode 100644 core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java diff --git a/core/src/main/antlr4/Metapath10.g4 b/core/src/main/antlr4/Metapath10.g4 index acea52be3..0fb07f3ca 100644 --- a/core/src/main/antlr4/Metapath10.g4 +++ b/core/src/main/antlr4/Metapath10.g4 @@ -4,11 +4,8 @@ parser grammar Metapath10; options { tokenVocab=Metapath10Lexer; superClass=Metapath10ParserBase; } -// Metapath extensions -metapath : expr EOF ; - // [1] -// xpath : expr EOF ; +metapath : expr EOF ; // paramlist : param ( COMMA param)* ; // param : DOLLAR eqname typedeclaration? ; // functionbody : enclosedexpr ; @@ -81,7 +78,7 @@ keyspecifier : NCName | IntegerLiteral | parenthesizedexpr | STAR ; //arrowfunctionspecifier : eqname | varref | parenthesizedexpr ; arrowfunctionspecifier : eqname; // primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall | functionitemexpr | mapconstructor | arrayconstructor | unarylookup ; -primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall | arrayconstructor | unarylookup; +primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall | mapconstructor | arrayconstructor | unarylookup; literal : numericliteral | StringLiteral ; numericliteral : IntegerLiteral | DecimalLiteral | DoubleLiteral ; varref : DOLLAR varname ; @@ -97,11 +94,11 @@ argument : exprsingle ; // functionitemexpr : namedfunctionref | inlinefunctionexpr ; // namedfunctionref : eqname POUND IntegerLiteral /* xgc: reserved-function-names */; // inlinefunctionexpr : KW_FUNCTION OP paramlist? CP ( KW_AS sequencetype)? functionbody ; -// mapconstructor : KW_MAP OC (mapconstructorentry ( COMMA mapconstructorentry)*)? CC ; +mapconstructor : KW_MAP OC (mapconstructorentry ( COMMA mapconstructorentry)*)? CC ; // [70] -// mapconstructorentry : mapkeyexpr COLON mapvalueexpr ; -// mapkeyexpr : exprsingle ; -// mapvalueexpr : exprsingle ; +mapconstructorentry : mapkeyexpr COLON mapvalueexpr ; +mapkeyexpr : exprsingle ; +mapvalueexpr : exprsingle ; arrayconstructor : squarearrayconstructor | curlyarrayconstructor ; squarearrayconstructor : OB (exprsingle ( COMMA exprsingle)*)? CB ; // [75] @@ -152,7 +149,7 @@ unarylookup : QM keyspecifier ; // Error in the spec. EQName also includes acceptable keywords. -eqname : QName | URIQualifiedName +eqname : NCName | QName | URIQualifiedName | 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 f95456ef4..21c735e2f 100644 --- a/core/src/main/antlr4/Metapath10Lexer.g4 +++ b/core/src/main/antlr4/Metapath10Lexer.g4 @@ -1,166 +1,180 @@ // This grammar is derived from the XPath 3.1 grammar produced by Ken Domino, et al (https://github.com/antlr/grammars-v4/blob/63359bd91593ece31a384acd507ae860d6cf7ff7/xpath/xpath31/XPath31Lexer.g4). +// This is a faithful implementation of the XPath version 3.1 grammar +// from the spec at https://www.w3.org/TR/2017/REC-xpath-31-20170321/ + +// $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine +// $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true + lexer grammar Metapath10Lexer; -AT : '@' ; -BANG : '!' ; -CB : ']' ; -CC : '}' ; -CEQ : ':=' ; -COLON : ':' ; -COLONCOLON : '::' ; -COMMA : ',' ; -CP : ')' ; -CS : ':*' ; -D : '.' ; -DD : '..' ; -DOLLAR : '$' ; -EG : '=>' ; -EQ : '=' ; -GE : '>=' ; -GG : '>>' ; -GT : '>' ; -LE : '<=' ; -LL : '<<' ; -LT : '<' ; -MINUS : '-' ; -NE : '!=' ; -OB : '[' ; -OC : '{' ; -OP : '(' ; -P : '|' ; -PLUS : '+' ; -POUND : '#' ; -PP : '||' ; -QM : '?' ; -SC : '*:' ; -SLASH : '/' ; -SS : '//' ; -STAR : '*' ; +AT : '@'; +BANG : '!'; +CB : ']'; +CC : '}'; +CEQ : ':='; +COLON : ':'; +COLONCOLON : '::'; +COMMA : ','; +CP : ')'; +CS : ':*'; +D : '.'; +DD : '..'; +DOLLAR : '$'; +EG : '=>'; +EQ : '='; +GE : '>='; +GG : '>>'; +GT : '>'; +LE : '<='; +LL : '<<'; +LT : '<'; +MINUS : '-'; +NE : '!='; +OB : '['; +OC : '{'; +OP : '('; +P : '|'; +PLUS : '+'; +POUND : '#'; +PP : '||'; +QM : '?'; +SC : '*:'; +SLASH : '/'; +SS : '//'; +STAR : '*'; // KEYWORDS -KW_ANCESTOR : 'ancestor' ; -KW_ANCESTOR_OR_SELF : 'ancestor-or-self' ; -KW_AND : 'and' ; -KW_ARRAY : 'array' ; -KW_AS : 'as' ; -KW_ATTRIBUTE : 'attribute' ; -KW_CAST : 'cast' ; -KW_CASTABLE : 'castable' ; -KW_CHILD : 'child' ; -KW_COMMENT : 'comment' ; -KW_DESCENDANT : 'descendant' ; -KW_DESCENDANT_OR_SELF : 'descendant-or-self' ; -KW_DIV : 'div' ; -KW_DOCUMENT_NODE : 'document-node' ; -KW_ELEMENT : 'element' ; -KW_ELSE : 'else' ; -KW_EMPTY_SEQUENCE : 'empty-sequence' ; -KW_EQ : 'eq' ; -KW_EVERY : 'every' ; -KW_EXCEPT : 'except' ; -KW_FOLLOWING : 'following' ; -KW_FOLLOWING_SIBLING : 'following-sibling' ; -KW_FOR : 'for' ; -KW_FUNCTION : 'function' ; -KW_GE : 'ge' ; -KW_GT : 'gt' ; -KW_IDIV : 'idiv' ; -KW_IF : 'if' ; -KW_IN : 'in' ; -KW_INSTANCE : 'instance' ; -KW_INTERSECT : 'intersect' ; -KW_IS : 'is' ; -KW_ITEM : 'item' ; -KW_LE : 'le' ; -KW_LET : 'let' ; -KW_LT : 'lt' ; -KW_MAP : 'map' ; -KW_MOD : 'mod' ; -KW_NAMESPACE : 'namespace' ; -KW_NAMESPACE_NODE : 'namespace-node' ; -KW_NE : 'ne' ; -KW_NODE : 'node' ; -KW_OF : 'of' ; -KW_OR : 'or' ; -KW_PARENT : 'parent' ; -KW_PRECEDING : 'preceding' ; -KW_PRECEDING_SIBLING : 'preceding-sibling' ; -KW_PROCESSING_INSTRUCTION : 'processing-instruction' ; -KW_RETURN : 'return' ; -KW_SATISFIES : 'satisfies' ; -KW_SCHEMA_ATTRIBUTE : 'schema-attribute' ; -KW_SCHEMA_ELEMENT : 'schema-element' ; -KW_SELF : 'self' ; -KW_SOME : 'some' ; -KW_TEXT : 'text' ; -KW_THEN : 'then' ; -KW_TO : 'to' ; -KW_TREAT : 'treat' ; -KW_UNION : 'union' ; +KW_ANCESTOR : 'ancestor'; +KW_ANCESTOR_OR_SELF : 'ancestor-or-self'; +KW_AND : 'and'; +KW_ARRAY : 'array'; +KW_AS : 'as'; +KW_ATTRIBUTE : 'attribute'; +KW_CAST : 'cast'; +KW_CASTABLE : 'castable'; +KW_CHILD : 'child'; +KW_COMMENT : 'comment'; +KW_DESCENDANT : 'descendant'; +KW_DESCENDANT_OR_SELF : 'descendant-or-self'; +KW_DIV : 'div'; +KW_DOCUMENT_NODE : 'document-node'; +KW_ELEMENT : 'element'; +KW_ELSE : 'else'; +KW_EMPTY_SEQUENCE : 'empty-sequence'; +KW_EQ : 'eq'; +KW_EVERY : 'every'; +KW_EXCEPT : 'except'; +KW_FOLLOWING : 'following'; +KW_FOLLOWING_SIBLING : 'following-sibling'; +KW_FOR : 'for'; +KW_FUNCTION : 'function'; +KW_GE : 'ge'; +KW_GT : 'gt'; +KW_IDIV : 'idiv'; +KW_IF : 'if'; +KW_IN : 'in'; +KW_INSTANCE : 'instance'; +KW_INTERSECT : 'intersect'; +KW_IS : 'is'; +KW_ITEM : 'item'; +KW_LE : 'le'; +KW_LET : 'let'; +KW_LT : 'lt'; +KW_MAP : 'map'; +KW_MOD : 'mod'; +KW_NAMESPACE : 'namespace'; +KW_NAMESPACE_NODE : 'namespace-node'; +KW_NE : 'ne'; +KW_NODE : 'node'; +KW_OF : 'of'; +KW_OR : 'or'; +KW_PARENT : 'parent'; +KW_PRECEDING : 'preceding'; +KW_PRECEDING_SIBLING : 'preceding-sibling'; +KW_PROCESSING_INSTRUCTION : 'processing-instruction'; +KW_RETURN : 'return'; +KW_SATISFIES : 'satisfies'; +KW_SCHEMA_ATTRIBUTE : 'schema-attribute'; +KW_SCHEMA_ELEMENT : 'schema-element'; +KW_SELF : 'self'; +KW_SOME : 'some'; +KW_TEXT : 'text'; +KW_THEN : 'then'; +KW_TO : 'to'; +KW_TREAT : 'treat'; +KW_UNION : 'union'; // A.2.1. TERMINAL SYMBOLS // This isn't a complete list of tokens in the language. // Keywords and symbols are terminals. -IntegerLiteral : FragDigits ; -DecimalLiteral : '.' FragDigits | FragDigits '.' [0-9]* ; -DoubleLiteral : ('.' FragDigits | FragDigits ('.' [0-9]*)?) [eE] [+-]? FragDigits ; -StringLiteral : '"' (~["] | FragEscapeQuot)* '"' | '\'' (~['] | FragEscapeApos)* '\'' ; -URIQualifiedName : BracedURILiteral NCName ; -BracedURILiteral : 'Q' '{' [^{}]* '}' ; +IntegerLiteral : FragDigits; +DecimalLiteral : '.' FragDigits | FragDigits '.' [0-9]*; +DoubleLiteral : ('.' FragDigits | FragDigits ('.' [0-9]*)?) [eE] [+-]? FragDigits; +StringLiteral : '"' (~["] | FragEscapeQuot)* '"' | '\'' (~['] | FragEscapeApos)* '\''; +URIQualifiedName : BracedURILiteral NCName; +BracedURILiteral : 'Q' '{' [^{}]* '}'; // Error in spec: EscapeQuot and EscapeApos are not terminals! -fragment FragEscapeQuot : '""' ; +fragment FragEscapeQuot : '""'; fragment FragEscapeApos : '\'\''; // Error in spec: Comment isn't really a terminal, but an off-channel object. -Comment : '(:' (Comment | CommentContents)*? ':)' -> skip ; -QName : FragQName ; -NCName : FragmentNCName ; +Comment : '(:' (Comment | CommentContents)*? ':)' -> skip; +NCName : FragmentNCName; +QName : FragQName; // Error in spec: Char is not a terminal! -fragment Char : FragChar ; -fragment FragDigits : [0-9]+ ; -fragment CommentContents : Char ; +fragment Char : FragChar; +fragment FragDigits : [0-9]+; +fragment CommentContents : Char; // https://www.w3.org/TR/REC-xml-names/#NT-QName -fragment FragQName : FragPrefixedName | FragUnprefixedName ; -fragment FragPrefixedName : FragPrefix ':' FragLocalPart ; -fragment FragUnprefixedName : FragLocalPart ; -fragment FragPrefix : FragmentNCName ; -fragment FragLocalPart : FragmentNCName ; -fragment FragNCNameStartChar - : 'A'..'Z' - | '_' - | 'a'..'z' - | '\u00C0'..'\u00D6' - | '\u00D8'..'\u00F6' - | '\u00F8'..'\u02FF' - | '\u0370'..'\u037D' - | '\u037F'..'\u1FFF' - | '\u200C'..'\u200D' - | '\u2070'..'\u218F' - | '\u2C00'..'\u2FEF' - | '\u3001'..'\uD7FF' - | '\uF900'..'\uFDCF' - | '\uFDF0'..'\uFFFD' - | '\u{10000}'..'\u{EFFFF}' - ; -fragment FragNCNameChar - : FragNCNameStartChar | '-' | '.' | '0'..'9' - | '\u00B7' | '\u0300'..'\u036F' - | '\u203F'..'\u2040' - ; -fragment FragmentNCName : FragNCNameStartChar FragNCNameChar* ; +fragment FragQName : FragPrefixedName | FragUnprefixedName; +fragment FragPrefixedName : FragPrefix ':' FragLocalPart; +fragment FragUnprefixedName : FragLocalPart; +fragment FragPrefix : FragmentNCName; +fragment FragLocalPart : FragmentNCName; +fragment FragNCNameStartChar: + 'A' ..'Z' + | '_' + | 'a' ..'z' + | '\u00C0' ..'\u00D6' + | '\u00D8' ..'\u00F6' + | '\u00F8' ..'\u02FF' + | '\u0370' ..'\u037D' + | '\u037F' ..'\u1FFF' + | '\u200C' ..'\u200D' + | '\u2070' ..'\u218F' + | '\u2C00' ..'\u2FEF' + | '\u3001' ..'\uD7FF' + | '\uF900' ..'\uFDCF' + | '\uFDF0' ..'\uFFFD' + | '\u{10000}' ..'\u{EFFFF}' +; +fragment FragNCNameChar: + FragNCNameStartChar + | '-' + | '.' + | '0' ..'9' + | '\u00B7' + | '\u0300' ..'\u036F' + | '\u203F' ..'\u2040' +; +fragment FragmentNCName: FragNCNameStartChar FragNCNameChar*; // https://www.w3.org/TR/REC-xml/#NT-Char -fragment FragChar : '\u0009' | '\u000a' | '\u000d' - | '\u0020'..'\ud7ff' - | '\ue000'..'\ufffd' - | '\u{10000}'..'\u{10ffff}' - ; +fragment FragChar: + '\u0009' + | '\u000a' + | '\u000d' + | '\u0020' ..'\ud7ff' + | '\ue000' ..'\ufffd' + | '\u{10000}' ..'\u{10ffff}' +; // https://github.com/antlr/grammars-v4/blob/17d3db3fd6a8fc319a12176e0bb735b066ec0616/xpath/xpath31/XPath31.g4#L389 -Whitespace : ('\u000d' | '\u000a' | '\u0020' | '\u0009')+ -> skip ; +Whitespace: ('\u000d' | '\u000a' | '\u0020' | '\u0009')+ -> skip; // Not per spec. Specified for testing. -SEMI : ';' ; +SEMI: ';'; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java index 1293ee9aa..58520adcc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java @@ -40,7 +40,9 @@ public interface ICollectionValue { @NonNull static Stream normalizeAsItems(@NonNull ICollectionValue value) { - return value instanceof IItem ? ObjectUtils.notNull(Stream.of((IItem) value)) : value.asSequence().stream(); + return value instanceof IItem + ? ObjectUtils.notNull(Stream.of((IItem) value)) + : value.asSequence().stream(); } @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java similarity index 98% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java index ec9b682db..0cae7cbcc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IStringValued.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java @@ -28,7 +28,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public interface IStringValued { +public interface IPrintable { /** * Get the string value. * diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java index b93b84050..09ae69ce0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ISequence.java @@ -60,7 +60,7 @@ * the Java type of the items in a sequence */ @SuppressWarnings("PMD.ShortMethodName") -public interface ISequence extends List, IStringValued, ICollectionValue { +public interface ISequence extends List, IPrintable, ICollectionValue { /** * Get an empty sequence. * @@ -154,7 +154,7 @@ default ITEM getFirstItem(boolean requireSingleton) { } @NonNull - default ICollectionValue toArrayMember() { + default ICollectionValue toCollectionValue() { ICollectionValue retval; switch (size()) { case 0: diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java index 115c0f63a..aa76b984b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathConstants.java @@ -55,6 +55,9 @@ public final class MetapathConstants { public static final URI NS_METAPATH_FUNCTIONS_ARRAY = ObjectUtils.requireNonNull( URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions/array")); @NonNull + public static final URI NS_METAPATH_FUNCTIONS_MAP = ObjectUtils.requireNonNull( + URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions/map")); + @NonNull public static final URI NS_METAPATH_FUNCTIONS_EXTENDED = NS_METAPATH; @NonNull @@ -67,6 +70,8 @@ public final class MetapathConstants { public static final String PREFIX_XPATH_FUNCTIONS_MATH = "math"; @NonNull public static final String PREFIX_XPATH_FUNCTIONS_ARRAY = "array"; + @NonNull + public static final String PREFIX_XPATH_FUNCTIONS_MAP = "map"; private MetapathConstants() { // disable construction diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java index 1e8b7ca17..9054e741a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/MetapathExpression.java @@ -46,6 +46,8 @@ import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.misc.ParseCancellationException; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.logging.log4j.LogManager; @@ -145,6 +147,13 @@ public static MetapathExpression compile(@NonNull String path, @NonNull StaticCo Metapath10 parser = new Metapath10(tokens); parser.removeErrorListeners(); parser.addErrorListener(new FailingErrorListener()); + parser.setErrorHandler(new DefaultErrorStrategy() { + + @Override + public void sync(Parser recognizer) { + // disable + } + }); ParseTree tree = ObjectUtils.notNull(parser.expr()); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java index 1859059a7..9356bc5fd 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticContext.java @@ -65,6 +65,9 @@ public final class StaticContext { knownNamespaces.put( MetapathConstants.PREFIX_XPATH_FUNCTIONS_ARRAY, MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY); + knownNamespaces.put( + MetapathConstants.PREFIX_XPATH_FUNCTIONS_MAP, + MetapathConstants.NS_METAPATH_FUNCTIONS_MAP); WELL_KNOWN_NAMESPACES = CollectionUtil.unmodifiableMap(knownNamespaces); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/AbstractAstVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/AbstractAstVisitor.java index b00d13711..1e4f40393 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/AbstractAstVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/AbstractAstVisitor.java @@ -54,6 +54,10 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LetexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LiteralContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LookupContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapconstructorContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapconstructorentryContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapkeyexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapvalueexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MetapathContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MultiplicativeexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.NametestContext; @@ -145,11 +149,9 @@ protected R delegateToChild(@NonNull T ctx) { throw new IllegalStateException("a single child expression was expected"); } - /* - * ============================================================ Expressions - - * https://www.w3.org/TR/xpath-31/#id-expressions - * ============================================================ - */ + // ============================================================ + // Expressions - https://www.w3.org/TR/xpath-31/#id-expressions + // ============================================================ @Override public R visitMetapath(MetapathContext ctx) { @@ -177,11 +179,10 @@ public R visitExprsingle(ExprsingleContext ctx) { assert ctx != null; return delegateToChild(ctx); } - /* - * ============================================================================ - * Primary Expressions - https://www.w3.org/TR/xpath-31/#id-primary-expressions - * ============================================================================ - */ + + // ============================================================================ + // Primary Expressions - https://www.w3.org/TR/xpath-31/#id-primary-expressions + // ============================================================================ @Override public R visitPrimaryexpr(PrimaryexprContext ctx) { @@ -189,11 +190,9 @@ public R visitPrimaryexpr(PrimaryexprContext ctx) { return delegateToChild(ctx); } - /* - * ================================================================= Literal - * Expressions - https://www.w3.org/TR/xpath-31/#id-literals - * ================================================================= - */ + // ================================================================= + // Literal Expressions - https://www.w3.org/TR/xpath-31/#id-literals + // ================================================================= /** * Handle the provided expression. @@ -225,11 +224,9 @@ public R visitNumericliteral(NumericliteralContext ctx) { return handle(ctx, (context) -> handleNumericLiteral(ctx)); } - /* - * ================================================================== Variable - * References - https://www.w3.org/TR/xpath-31/#id-variables - * ================================================================== - */ + // ================================================================== + // Variable References - https://www.w3.org/TR/xpath-31/#id-variables + // ================================================================== /** * Handle the provided expression. @@ -252,13 +249,10 @@ public R visitVarname(VarnameContext ctx) { return delegateToChild(ctx); } - /* - * ============================================================================= - * ==== Parenthesized Expressions - - * https://www.w3.org/TR/xpath-31/#id-paren-expressions - * ============================================================================= - * ==== - */ + // ==================================================== + // Parenthesized Expressions - + // https://www.w3.org/TR/xpath-31/#id-paren-expressions + // ==================================================== /** * Handle the provided expression. @@ -276,13 +270,10 @@ public R visitParenthesizedexpr(ParenthesizedexprContext ctx) { return expr == null ? handleEmptyParenthesizedexpr(ctx) : visit(expr); } - /* - * ============================================================================= - * ======== Context Item Expression - - * https://www.w3.org/TR/xpath-31/#id-context-item-expression - * ============================================================================= - * ======== - */ + // ========================================================== + // Context Item Expression - + // https://www.w3.org/TR/xpath-31/#id-context-item-expression + // ========================================================== /** * Handle the provided expression. @@ -340,11 +331,9 @@ public R visitEnclosedexpr(EnclosedexprContext ctx) { return expr == null ? null : expr.accept(this); } - /* - * ========================================================================= - * Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression - * ========================================================================= - */ + // ========================================================================= + // Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression + // ========================================================================= /** * Handle the provided expression. @@ -391,11 +380,9 @@ public R visitLookup(LookupContext ctx) { return handleLookup(ctx); } - /* - * ====================================================================== Path - * Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions - * ====================================================================== - */ + // ====================================================================== + // Path Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions + // ====================================================================== /** * Handle the provided expression. @@ -412,13 +399,10 @@ public R visitPathexpr(PathexprContext ctx) { return handle(ctx, (context) -> handlePathexpr(ctx)); } - /* - * ============================================================================= - * ========== RelativePath Expressions - - * https://www.w3.org/TR/xpath-31/#id-relative-path-expressions - * ============================================================================= - * ========== - */ + // ============================================================ + // RelativePath Expressions - + // https://www.w3.org/TR/xpath-31/#id-relative-path-expressions + // ============================================================ /** * Handle the provided expression. @@ -435,11 +419,9 @@ public R visitRelativepathexpr(RelativepathexprContext ctx) { return handle(ctx, (context) -> handleRelativepathexpr(ctx)); } - /* - * ================================================ Steps - - * https://www.w3.org/TR/xpath-31/#id-steps - * ================================================ - */ + // ================================================ + // Steps - https://www.w3.org/TR/xpath-31/#id-steps + // ================================================ @Override public R visitStepexpr(StepexprContext ctx) { @@ -479,11 +461,9 @@ public R visitReversestep(ReversestepContext ctx) { return handle(ctx, (context) -> handleReversestep(ctx)); } - /* - * ====================================================================== - * Predicates within Steps - https://www.w3.org/TR/xpath-31/#id-predicate - * ====================================================================== - */ + // ====================================================================== + // Predicates within Steps - https://www.w3.org/TR/xpath-31/#id-predicate + // ====================================================================== /** * Handle the provided expression. @@ -506,11 +486,9 @@ public R visitPredicatelist(PredicatelistContext ctx) { throw new IllegalStateException(); } - /* - * =========================================== Axes - - * https://www.w3.org/TR/xpath-31/#axes - * =========================================== - */ + // =========================================== + // Axes - https://www.w3.org/TR/xpath-31/#axes + // =========================================== @Override public R visitForwardaxis(ForwardaxisContext ctx) { @@ -524,11 +502,9 @@ public R visitReverseaxis(ReverseaxisContext ctx) { throw new IllegalStateException(); } - /* - * ======================================================= Node Tests - - * https://www.w3.org/TR/xpath-31/#node-tests - * ======================================================= - */ + // ======================================================= + // Node Tests - https://www.w3.org/TR/xpath-31/#node-tests + // ======================================================= @Override public R visitNodetest(NodetestContext ctx) { @@ -563,11 +539,9 @@ public R visitWildcard(WildcardContext ctx) { return handleWildcard(ctx); } - /* - * =========================================================== Abbreviated - * Syntax - https://www.w3.org/TR/xpath-31/#abbrev - * =========================================================== - */ + // =========================================================== + // Abbreviated Syntax - https://www.w3.org/TR/xpath-31/#abbrev + // =========================================================== /** * Handle the provided expression. @@ -599,11 +573,9 @@ public R visitAbbrevreversestep(AbbrevreversestepContext ctx) { return handleAbbrevreversestep(ctx); } - /* - * ====================================================================== - * Constructing Sequences - https://www.w3.org/TR/xpath-31/#construct_seq - * ====================================================================== - */ + // ====================================================================== + // Constructing Sequences - https://www.w3.org/TR/xpath-31/#construct_seq + // ====================================================================== /** * Handle the provided expression. @@ -620,11 +592,9 @@ public R visitRangeexpr(RangeexprContext ctx) { return handle(ctx, (context) -> handleRangeexpr(ctx)); } - /* - * ======================================================================== - * Combining Node Sequences - https://www.w3.org/TR/xpath-31/#combining_seq - * ======================================================================== - */ + // ======================================================================== + // Combining Node Sequences - https://www.w3.org/TR/xpath-31/#combining_seq + // ======================================================================== /** * Handle the provided expression. @@ -656,11 +626,9 @@ public R visitIntersectexceptexpr(IntersectexceptexprContext ctx) { return handle(ctx, (context) -> handleIntersectexceptexpr(ctx)); } - /* - * ====================================================================== - * Arithmetic Expressions - https://www.w3.org/TR/xpath-31/#id-arithmetic - * ====================================================================== - */ + // ====================================================================== + // Arithmetic Expressions - https://www.w3.org/TR/xpath-31/#id-arithmetic + // ====================================================================== /** * Handle the provided expression. @@ -713,13 +681,10 @@ public R visitValueexpr(ValueexprContext ctx) { return delegateToChild(ctx); } - /* - * ============================================================================= - * =========== String Concatenation Expressions - - * https://www.w3.org/TR/xpath-31/#id-string-concat-expr - * ============================================================================= - * =========== - */ + // ===================================================== + // String Concatenation Expressions - + // https://www.w3.org/TR/xpath-31/#id-string-concat-expr + // ===================================================== /** * Handle the provided expression. @@ -736,11 +701,9 @@ public R visitStringconcatexpr(StringconcatexprContext ctx) { return handle(ctx, (context) -> handleStringconcatexpr(ctx)); } - /* - * ======================================================================= - * Comparison Expressions - https://www.w3.org/TR/xpath-31/#id-comparisons - * ======================================================================= - */ + // ======================================================================= + // Comparison Expressions - https://www.w3.org/TR/xpath-31/#id-comparisons + // ======================================================================= /** * Handle the provided expression. @@ -769,11 +732,9 @@ public R visitGeneralcomp(GeneralcompContext ctx) { throw new IllegalStateException(); } - /* - * ============================================================================ - * Logical Expressions - https://www.w3.org/TR/xpath-31/#id-logical-expressions - * ============================================================================ - */ + // ============================================================================ + // Logical Expressions - https://www.w3.org/TR/xpath-31/#id-logical-expressions + // ============================================================================ /** * Handle the provided expression. @@ -867,6 +828,43 @@ public R visitSimpleletbinding(SimpleletbindingContext ctx) { throw new IllegalStateException(); } + // ====================================================================== + // Map Constructors - https://www.w3.org/TR/xpath-31/#id-map-constructors + // ====================================================================== + + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result + */ + protected abstract R handleMapConstructor(@NonNull MapconstructorContext ctx); + + @Override + public R visitMapconstructor(MapconstructorContext ctx) { + assert ctx != null; + return handleMapConstructor(ctx); + } + + @Override + public R visitMapconstructorentry(MapconstructorentryContext ctx) { + // should never be called, since this is handled by the parent expression + throw new IllegalStateException(); + } + + @Override + public R visitMapkeyexpr(MapkeyexprContext ctx) { + assert ctx != null; + return delegateToChild(ctx); + } + + @Override + public R visitMapvalueexpr(MapvalueexprContext ctx) { + assert ctx != null; + return delegateToChild(ctx); + } + // ============================================================== // Array Constructors - https://www.w3.org/TR/xpath-31/#id-arrays // ============================================================== diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractCSTVisitorBase.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractCSTVisitorBase.java index cf9dd621d..6c8142bbb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractCSTVisitorBase.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractCSTVisitorBase.java @@ -132,20 +132,20 @@ public IExpression visit(ParseTree tree) { } @Nullable - protected IExpression - nAiryToCollection( + protected + R nAiryToCollection( @NonNull CONTEXT context, int startIndex, int step, - @NonNull BiFunction parser, - @NonNull Function, IExpression> supplier) { + @NonNull BiFunction parser, + @NonNull Function, R> supplier) { int numChildren = context.getChildCount(); - IExpression retval = null; + R retval = null; if (startIndex < numChildren) { - List children = new ArrayList<>((numChildren - startIndex) / step); + List children = new ArrayList<>((numChildren - startIndex) / step); for (int idx = startIndex; idx < numChildren; idx += step) { - EXPRESSION result = parser.apply(context, idx); + T result = parser.apply(context, idx); children.add(result); } retval = supplier.apply(children); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpressionVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpressionVisitor.java index 9708e7542..1f7a336f6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpressionVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpressionVisitor.java @@ -354,12 +354,22 @@ public RESULT visitSimpleMap(SimpleMap expr, CONTEXT context) { } @Override - public RESULT visitArray(ArraySequence expr, CONTEXT context) { + public RESULT visitMapConstructor(MapConstructor expr, CONTEXT context) { return visitChildren(expr, context); } @Override - public RESULT visitArray(ArraySquare expr, CONTEXT context) { + public RESULT visitMapConstructorEntry(MapConstructor.Entry expr, CONTEXT context) { + return visitChildren(expr, context); + } + + @Override + public RESULT visitArray(ArraySequenceConstructor expr, CONTEXT context) { + return visitChildren(expr, context); + } + + @Override + public RESULT visitArray(ArraySquareConstructor expr, CONTEXT context) { return visitChildren(expr, context); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractLookup.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractLookup.java index e61f89fed..5bb77d4b5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractLookup.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractLookup.java @@ -30,12 +30,17 @@ 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.ArrayGet; import gov.nist.secauto.metaschema.core.metapath.function.library.FnData; +import gov.nist.secauto.metaschema.core.metapath.function.library.MapGet; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.stream.Stream; @@ -63,6 +68,8 @@ default Stream lookup( Stream result; if (item instanceof IArrayItem) { result = lookupInArray((IArrayItem) item, dynamicContext, focus); + } else if (item instanceof IMapItem) { + result = lookupInMap((IMapItem) item, dynamicContext, focus); } else { throw new InvalidTypeMetapathException(item, String.format("Item type '%s' is not an array or map.", item.getClass().getName())); @@ -70,10 +77,17 @@ default Stream lookup( return result; } + @NonNull Stream lookupInArray( @NonNull IArrayItem item, @NonNull DynamicContext dynamicContext, @NonNull ISequence focus); + + @NonNull + Stream lookupInMap( + @NonNull IMapItem item, + @NonNull DynamicContext dynamicContext, + @NonNull ISequence focus); } protected static class NCNameKeySpecifier implements IKeySpecifier { @@ -96,13 +110,21 @@ public Stream lookupInArray( throw new InvalidTypeMetapathException(item, String.format("The key name-based lookup '%s' is not appropriate for an array.", getName())); } + + @Override + public Stream lookupInMap( + IMapItem item, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(Stream.ofNullable(MapGet.get(item, IStringItem.valueOf(name)))); + } } protected static class IntegerLiteralKeySpecifier implements IKeySpecifier { private final int index; public IntegerLiteralKeySpecifier(IIntegerItem literal) { - index = literal.asInteger().intValueExact() - 1; + index = literal.asInteger().intValueExact(); } @Override @@ -111,7 +133,7 @@ public Stream lookupInArray( DynamicContext dynamicContext, ISequence focus) { try { - return Stream.of(item.get(index)); + return ObjectUtils.notNull(Stream.ofNullable(ArrayGet.get(item, index))); } catch (IndexOutOfBoundsException ex) { throw new ArrayException( ArrayException.INDEX_OUT_OF_BOUNDS, @@ -121,6 +143,14 @@ public Stream lookupInArray( ex); } } + + @Override + public Stream lookupInMap( + IMapItem item, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(Stream.ofNullable(MapGet.get(item, IIntegerItem.valueOf(index)))); + } } protected static class WildcardKeySpecifier implements IKeySpecifier { @@ -130,7 +160,15 @@ public Stream lookupInArray( IArrayItem item, DynamicContext dynamicContext, ISequence focus) { - return item.stream(); + return ObjectUtils.notNull(item.stream()); + } + + @Override + public Stream lookupInMap( + IMapItem item, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(item.values().stream()); } } @@ -147,18 +185,18 @@ public IExpression getKeyExpression() { } @Override - public Stream lookupInArray( + public Stream lookupInArray( IArrayItem item, DynamicContext dynamicContext, ISequence focus) { ISequence keys = FnData.fnData(getKeyExpression().accept(dynamicContext, focus)); - return keys.stream() - .map(key -> { + return ObjectUtils.notNull(keys.stream() + .flatMap(key -> { if (key instanceof IIntegerItem) { - int index = ((IIntegerItem) key).asInteger().intValueExact() - 1; + int index = ((IIntegerItem) key).asInteger().intValueExact(); try { - return item.get(index); + return Stream.ofNullable(ArrayGet.get(item, index)); } catch (IndexOutOfBoundsException ex) { throw new ArrayException( ArrayException.INDEX_OUT_OF_BOUNDS, @@ -173,8 +211,21 @@ public Stream lookupInArray( key.asString(), key.getClass().getName())); + })); + } + + @Override + public Stream lookupInMap( + IMapItem item, + DynamicContext dynamicContext, + ISequence focus) { + ISequence keys + = ObjectUtils.requireNonNull(FnData.fnData(getKeyExpression().accept(dynamicContext, focus))); + + return keys.stream() + .flatMap(key -> { + return Stream.ofNullable(MapGet.get(item, key)); }); } } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequence.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java similarity index 95% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequence.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java index cdf18b221..6b837b524 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequence.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java @@ -34,11 +34,11 @@ import edu.umd.cs.findbugs.annotations.Nullable; -public class ArraySequence implements IExpression { +public class ArraySequenceConstructor implements IExpression { @Nullable private final IExpression expr; - public ArraySequence(@Nullable IExpression expr) { + public ArraySequenceConstructor(@Nullable IExpression expr) { this.expr = expr; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquare.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java similarity index 93% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquare.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java index 35dd54ef0..6a98a508a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquare.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java @@ -35,11 +35,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public class ArraySquare implements IExpression { +public class ArraySquareConstructor implements IExpression { @NonNull private final List children; - public ArraySquare(@NonNull List children) { + public ArraySquareConstructor(@NonNull List children) { this.children = children; } @@ -52,7 +52,7 @@ public List getChildren() { public ISequence accept(DynamicContext dynamicContext, ISequence focus) { return ISequence.of(getChildren().stream() .map(expr -> expr.accept(dynamicContext, focus)) - .map(ISequence::toArrayMember) + .map(ISequence::toCollectionValue) .collect(IArrayItem.toArrayItem())); } @@ -60,5 +60,4 @@ public ISequence accept(DynamicContext dynamicContext, ISequenc public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { return visitor.visitArray(this, context); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java index 2f3f254dd..01b94a48f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/BuildCSTVisitor.java @@ -52,6 +52,8 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LetexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LiteralContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.LookupContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapconstructorContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MapconstructorentryContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.MultiplicativeexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.NametestContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.NodetestContext; @@ -265,32 +267,54 @@ protected IExpression handleLet(LetexprContext context) { return retval; } + // ====================================================================== + // Map Constructors - https://www.w3.org/TR/xpath-31/#id-map-constructors + // ====================================================================== + + @Override + protected MapConstructor handleMapConstructor(MapconstructorContext context) { + return context.getChildCount() == 2 + // empty + ? new MapConstructor(CollectionUtil.emptyList()) + // with members + : nAiryToCollection(context, 3, 2, + (ctx, idx) -> { + int pos = (idx - 3) / 2; + MapconstructorentryContext entry = ctx.mapconstructorentry(pos); + assert entry != null; + return new MapConstructor.Entry(entry.mapkeyexpr().accept(this), entry.mapvalueexpr().accept(this)); + }, + children -> { + assert children != null; + return new MapConstructor(children); + }); + } + // ============================================================== // Array Constructors - https://www.w3.org/TR/xpath-31/#id-arrays // ============================================================== @Override protected IExpression handleArrayConstructor(SquarearrayconstructorContext context) { - if (context.getChildCount() == 2) { - // empty - return new ArraySquare(CollectionUtil.emptyList()); - } - - return nAiryToCollection(context, 1, 2, - (ctx, idx) -> { - int pos = (idx - 1) / 2; - ParseTree tree = ctx.exprsingle(pos); - return visit(tree); - }, - children -> { - assert children != null; - return new ArraySquare(children); - }); + return context.getChildCount() == 2 + // empty + ? new ArraySquareConstructor(CollectionUtil.emptyList()) + // with members + : nAiryToCollection(context, 1, 2, + (ctx, idx) -> { + int pos = (idx - 1) / 2; + ParseTree tree = ctx.exprsingle(pos); + return visit(tree); + }, + children -> { + assert children != null; + return new ArraySquareConstructor(children); + }); } @Override protected IExpression handleArrayConstructor(CurlyarrayconstructorContext ctx) { - return new ArraySequence(visit(ctx.enclosedexpr())); + return new ArraySequenceConstructor(visit(ctx.enclosedexpr())); } // =============================================== diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/CSTPrinter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/CSTPrinter.java index 44a842c47..91af12d3f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/CSTPrinter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/CSTPrinter.java @@ -337,12 +337,12 @@ public String visitSimpleMap(SimpleMap expr, State context) { } @Override - public String visitArray(ArraySequence expr, State context) { + public String visitArray(ArraySequenceConstructor expr, State context) { return appendNode(expr, super.visitArray(expr, context), context); } @Override - public String visitArray(ArraySquare expr, State context) { + public String visitArray(ArraySquareConstructor expr, State context) { return appendNode(expr, super.visitArray(expr, context), context); } @@ -361,6 +361,15 @@ public String visitUnaryLookup(UnaryLookup expr, State context) { return appendNode(expr, super.visitUnaryLookup(expr, context), context); } + @Override + public String visitMapConstructor(MapConstructor expr, State context) { + return appendNode(expr, super.visitMapConstructor(expr, context), context); + } + + @Override + public String visitMapConstructorEntry(MapConstructor.Entry expr, State context) { + return appendNode(expr, super.visitMapConstructorEntry(expr, context), context); + } } static class State { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java index 39b2213b7..ed0eeb0a6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java @@ -27,10 +27,16 @@ 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.function.library.ArrayGet; +import gov.nist.secauto.metaschema.core.metapath.function.library.FnData; +import gov.nist.secauto.metaschema.core.metapath.function.library.MapGet; import gov.nist.secauto.metaschema.core.metapath.item.IItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; import java.util.List; @@ -75,10 +81,20 @@ public List getChildren() { @Override public ISequence accept(DynamicContext dynamicContext, ISequence focus) { - ISequence collection = getBase().accept(dynamicContext, focus); - ISequence key = getArgument().accept(dynamicContext, focus); + ISequence target = getBase().accept(dynamicContext, focus); + IItem collection = target.getFirstItem(true); + IAnyAtomicItem key = FnData.fnData(getArgument().accept(dynamicContext, focus)).getFirstItem(false); - return ArrayGet.SIGNATURE.execute(ObjectUtils.notNull(List.of(collection, key)), dynamicContext, focus); + ICollectionValue retval; + if (collection instanceof IArrayItem) { + retval = ArrayGet.get((IArrayItem) collection, IIntegerItem.cast(key)); + } else if (collection instanceof IMapItem) { + retval = MapGet.get((IMapItem) collection, IIntegerItem.cast(key)); + } else { + retval = null; + } + + return retval == null ? ISequence.empty() : retval.asSequence(); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IExpressionVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IExpressionVisitor.java index 633120ab1..3e487e761 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IExpressionVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IExpressionVisitor.java @@ -520,7 +520,7 @@ public interface IExpressionVisitor { * the processing context * @return the visitation result or {@code null} if no result was produced */ - RESULT visitArray(@NonNull ArraySequence expr, @NonNull CONTEXT context); + RESULT visitMapConstructor(@NonNull MapConstructor expr, @NonNull CONTEXT context); /** * Visit the CST node. @@ -531,7 +531,29 @@ public interface IExpressionVisitor { * the processing context * @return the visitation result or {@code null} if no result was produced */ - RESULT visitArray(@NonNull ArraySquare expr, @NonNull CONTEXT context); + RESULT visitMapConstructorEntry(@NonNull MapConstructor.Entry expr, @NonNull CONTEXT context); + + /** + * Visit the CST node. + * + * @param expr + * the CST node to visit + * @param context + * the processing context + * @return the visitation result or {@code null} if no result was produced + */ + RESULT visitArray(@NonNull ArraySequenceConstructor expr, @NonNull CONTEXT context); + + /** + * Visit the CST node. + * + * @param expr + * the CST node to visit + * @param context + * the processing context + * @return the visitation result or {@code null} if no result was produced + */ + RESULT visitArray(@NonNull ArraySquareConstructor expr, @NonNull CONTEXT context); /** * Visit the CST node. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/MapConstructor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/MapConstructor.java new file mode 100644 index 000000000..61bc9dc42 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/MapConstructor.java @@ -0,0 +1,114 @@ +/* + * 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.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.function.library.FnData; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class MapConstructor implements IExpression { + @NonNull + private final List entries; + + public MapConstructor(@NonNull List entries) { + this.entries = entries; + } + + @Override + public List getChildren() { + return entries; + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + return IMapItem.ofCollection( + ObjectUtils.notNull(getChildren().stream() + .map(item -> { + IAnyAtomicItem key + = FnData.fnData(item.getKeyExpression().accept(dynamicContext, focus)).getFirstItem(true); + ICollectionValue value = item.getValueExpression().accept(dynamicContext, focus).toCollectionValue(); + + return IMapItem.entry(key, value); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))) + .asSequence(); + } + + @Override + public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitMapConstructor(this, context); + } + + public static class Entry implements IExpression { + @NonNull + private final IExpression keyExpression; + @NonNull + private final IExpression valueExpression; + + public Entry(@NonNull IExpression keyExpression, @NonNull IExpression valueExpression) { + this.keyExpression = keyExpression; + this.valueExpression = valueExpression; + } + + @NonNull + public IExpression getKeyExpression() { + return keyExpression; + } + + @NonNull + public IExpression getValueExpression() { + return valueExpression; + } + + @SuppressWarnings("null") + @Override + public List getChildren() { + return List.of(keyExpression, valueExpression); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + throw new UnsupportedOperationException("handled by the map constructor"); + } + + @Override + public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitMapConstructorEntry(this, context); + } + + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppend.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppend.java index e1ad07b69..109d847dc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppend.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppend.java @@ -70,7 +70,7 @@ private static ISequence> execute(@No IItem focus) { IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( arguments.get(0).getFirstItem(true))); - @SuppressWarnings("unchecked") T appendage = (T) arguments.get(1).toArrayMember(); + @SuppressWarnings("unchecked") T appendage = (T) arguments.get(1).toCollectionValue(); return ISequence.of(append(array, appendage)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java index 680b3e6df..2e66a0aff 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java @@ -93,8 +93,13 @@ private static ISequence execute(@NonNull IFunction function, public static T get( @NonNull List target, @NonNull IIntegerItem positionItem) { - int position = positionItem.asInteger().intValue(); + return get(target, positionItem.asInteger().intValue()); + } + @NonNull + public static T get( + @NonNull List target, + int position) { try { return ObjectUtils.requireNonNull(target.get(position - 1)); } catch (IndexOutOfBoundsException ex) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java index a2f3cea92..36227f8f2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java @@ -77,7 +77,7 @@ private static ISequence> execute(@No IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( arguments.get(0).getFirstItem(true))); IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); - @SuppressWarnings("unchecked") T member = (T) ObjectUtils.requireNonNull(arguments.get(2)).toArrayMember(); + @SuppressWarnings("unchecked") T member = (T) ObjectUtils.requireNonNull(arguments.get(2)).toCollectionValue(); return ISequence.of(insertBefore(array, position, member)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java index cea736e1e..c10e1dacf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java @@ -78,7 +78,7 @@ private static ISequence> e IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( arguments.get(0).getFirstItem(true))); IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); - @SuppressWarnings("unchecked") T member = (T) arguments.get(2).toArrayMember(); + @SuppressWarnings("unchecked") T member = (T) arguments.get(2).toCollectionValue(); return put(array, position, member).asSequence(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java index 13ea87d4e..a3f9b24f4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/DefaultFunctionLibrary.java @@ -225,6 +225,9 @@ public DefaultFunctionLibrary() { // NOPMD - intentional // https://www.w3.org/TR/xpath-functions-31/#func-array-flatten registerFunction(ArrayFlatten.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-get + registerFunction(MapGet.SIGNATURE); + // xpath casting functions registerFunction( CastFunction.signature(MetapathConstants.NS_XML_SCHEMA, "boolean", IBooleanItem.class, IBooleanItem::cast)); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGet.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGet.java new file mode 100644 index 000000000..4e6ed5863 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGet.java @@ -0,0 +1,100 @@ +/* + * 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.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.ArrayException; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +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 MapGet { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("get") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("key") + .type(IAnyAtomicItem.class) + .one() + .build()) + .returnType(IItem.class) + .returnZeroOrOne() + .functionHandler(MapGet::execute) + .build(); + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IMapItem map = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + IAnyAtomicItem key = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + + ICollectionValue value = get(map, key); + return value == null ? ISequence.empty() : value.asSequence(); + } + + /** + * An implementation of XPath 3.1 array:get. + * + * @param + * the type of items in the given Metapath array + * @param map + * the array of Metapath items that is the target of retrieval + * @param key + * the integer position of the item to retrieve + * @return the retrieved item + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @Nullable + public static V get( + @NonNull IMapItem map, + @NonNull IAnyAtomicItem key) { + return map.get(key.asMapKey()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java new file mode 100644 index 000000000..41ab230cd --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java @@ -0,0 +1,105 @@ +/* + * 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.impl; + +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.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.ISequenceType; +import gov.nist.secauto.metaschema.core.metapath.function.Occurrence; +import gov.nist.secauto.metaschema.core.metapath.function.library.MapGet; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractMapItem + extends ImmutableCollections.AbstractImmutableDelegatedMap + implements IMapItem { + @NonNull + public static final QName QNAME = new QName("map"); + @NonNull + public static final Set PROPERTIES = ObjectUtils.notNull( + EnumSet.of(FunctionProperty.DETERMINISTIC)); + @NonNull + public static final List ARGUMENTS = ObjectUtils.notNull(List.of( + IArgument.builder().name("key").type(IAnyAtomicItem.class).one().build())); + @NonNull + public static final ISequenceType RESULT = ISequenceType.of(IAnyAtomicItem.class, Occurrence.ZERO_OR_ONE); + + @NonNull + private static final IMapItem EMPTY = new MapItemN<>(); + + @SuppressWarnings("unchecked") + @NonNull + public static IMapItem empty() { + return (IMapItem) EMPTY; + } + + @Override + public ISequence execute(List> arguments, DynamicContext dynamicContext, + ISequence focus) { + ISequence arg = FunctionUtils.asType( + ObjectUtils.notNull(arguments.get(0))); + + IAnyAtomicItem key = arg.getFirstItem(true); + if (key == null) { + return ISequence.empty(); // NOPMD - readability + } + + ICollectionValue result = MapGet.get(this, key); + return result == null ? ISequence.empty() : result.asSequence(); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + @Override + public boolean equals(Object other) { + return other == this + || other instanceof IMapItem && getValue().equals(((IMapItem) other).getValue()); + } + + @Override + public String asString() { + return ObjectUtils.notNull(toString()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java new file mode 100644 index 000000000..83bb7b822 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java @@ -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.impl; + +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + +public abstract class AbstractStringMapKey + implements IMapKey { + + @Override + public int hashCode() { + return getKey().asStringItem().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof AbstractStringMapKey + && getKey().asStringItem().equals(((AbstractStringMapKey) obj).getKey().asStringItem())); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java index 4102d577d..897bd6257 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java @@ -29,10 +29,16 @@ import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.AbstractCollection; +import java.util.AbstractMap; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; @@ -47,7 +53,7 @@ protected static UnsupportedOperationException unsupported() { return new UnsupportedOperationException("method not supported"); } - public static abstract class AbstractImmutableCollection + public abstract static class AbstractImmutableCollection extends AbstractCollection { @Override @@ -86,7 +92,7 @@ public final boolean retainAll(Collection c) { } } - public static abstract class AbstractImmutableList + public abstract static class AbstractImmutableList extends AbstractImmutableCollection implements List { @@ -111,7 +117,7 @@ public T remove(int index) { } } - public static abstract class AbstractImmutableDelegatedCollection + public abstract static class AbstractImmutableDelegatedCollection extends AbstractImmutableList { @NonNull @@ -167,4 +173,84 @@ public String toString() { return getValue().toString(); } } + + public abstract static class AbstractImmutableMap + extends AbstractMap { + @Override + public void clear() { + throw unsupported(); + } + + @Override + public V compute(K key, BiFunction rf) { + throw unsupported(); + } + + @Override + public V computeIfAbsent(K key, Function mf) { + throw unsupported(); + } + + @Override + public V computeIfPresent(K key, BiFunction rf) { + throw unsupported(); + } + + @Override + public V merge(K key, V value, BiFunction rf) { + throw unsupported(); + } + + @Override + public V put(K key, V value) { + throw unsupported(); + } + + @Override + public void putAll(Map m) { + throw unsupported(); + } + + @Override + public V putIfAbsent(K key, V value) { + throw unsupported(); + } + + @Override + public V remove(Object key) { + throw unsupported(); + } + + @Override + public boolean remove(Object key, Object value) { + throw unsupported(); + } + + @Override + public V replace(K key, V value) { + throw unsupported(); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + throw unsupported(); + } + + @Override + public void replaceAll(BiFunction f) { + throw unsupported(); + } + } + + public abstract static class AbstractImmutableDelegatedMap + extends AbstractImmutableMap { + + @NonNull + public abstract Map getValue(); + + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(getValue().entrySet()); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java new file mode 100644 index 000000000..0085a25b6 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java @@ -0,0 +1,57 @@ +/* + * 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.impl; + +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Map; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class MapItemN + extends AbstractMapItem { + @NonNull + private final Map entries; + + @SafeVarargs + public MapItemN(@NonNull Map.Entry... entries) { + this(ObjectUtils.notNull(Map.ofEntries(entries))); + } + + public MapItemN(@NonNull Map entries) { + + this.entries = CollectionUtil.unmodifiableMap(entries); + } + + @Override + public Map getValue() { + return entries; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java index 8f4649a2e..17e71cab0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java @@ -29,7 +29,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractDateItem - extends AbstractAnyAtomicItem + extends AbstractTemporalItem implements IDateItem { /** * Construct a new item with the provided {@code value}. @@ -41,6 +41,11 @@ protected AbstractDateItem(@NonNull TYPE value) { super(value); } + @Override + public boolean hasTimezone() { + return true; + } + @Override public int hashCode() { return asZonedDateTime().hashCode(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java index 9de39e709..9778e8dcc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java @@ -29,7 +29,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractDateTimeItem - extends AbstractAnyAtomicItem + extends AbstractTemporalItem implements IDateTimeItem { /** * Construct a new item with the provided {@code value}. @@ -41,6 +41,11 @@ protected AbstractDateTimeItem(@NonNull TYPE value) { super(value); } + @Override + public boolean hasTimezone() { + return true; + } + @Override public int hashCode() { return asZonedDateTime().hashCode(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java new file mode 100644 index 000000000..8ba4daf57 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java @@ -0,0 +1,66 @@ +/* + * 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.item.atomic; + +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractDecimalItem + extends AbstractAnyAtomicItem + implements IDecimalItem { + + protected AbstractDecimalItem(@NonNull TYPE value) { + super(value); + } + + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + + private final class MapKey + implements IMapKey { + + @Override + public IDecimalItem getKey() { + return AbstractDecimalItem.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof AbstractDecimalItem.MapKey + && getKey().asDecimal().equals(((AbstractDecimalItem.MapKey) obj).getKey().asDecimal())); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java index 56a181b26..427943c1c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java @@ -33,7 +33,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractIntegerItem - extends AbstractAnyAtomicItem + extends AbstractDecimalItem implements IIntegerItem { /** * Construct a new item with the provided {@code value}. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java index 5c927cc32..3da0f8d2d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java @@ -26,6 +26,9 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractStringItem @@ -47,6 +50,11 @@ public String asString() { return getValue(); } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return asString().hashCode(); @@ -58,4 +66,13 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IStringItem && compareTo((IStringItem) obj) == 0); } + + private final class MapKey + extends AbstractStringMapKey { + + @Override + public IStringItem getKey() { + return AbstractStringItem.this; + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java new file mode 100644 index 000000000..d4753a5d0 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java @@ -0,0 +1,74 @@ +/* + * 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.item.atomic; + +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractTemporalItem + extends AbstractAnyAtomicItem + implements ITemporalItem { + + protected AbstractTemporalItem(@NonNull TYPE value) { + super(value); + } + + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + + private final class MapKey + implements IMapKey { + + @Override + public ITemporalItem getKey() { + return AbstractTemporalItem.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return this == obj; + } + + if (!(obj instanceof AbstractTemporalItem.MapKey)) { + return false; + } + + AbstractTemporalItem.MapKey other = (AbstractTemporalItem.MapKey) obj; + return hasTimezone() == other.getKey().hasTimezone() + && getKey().equals(other.getKey()); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java new file mode 100644 index 000000000..4435fd7a6 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUntypedAtomicItem.java @@ -0,0 +1,55 @@ +/* + * 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.item.atomic; + +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public abstract class AbstractUntypedAtomicItem + extends AbstractAnyAtomicItem + implements IUntypedAtomicItem { + + protected AbstractUntypedAtomicItem(@NonNull TYPE value) { + super(value); + } + + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + + private final class MapKey + extends AbstractStringMapKey { + + @Override + public IUntypedAtomicItem getKey() { + return AbstractUntypedAtomicItem.this; + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java index f83932e31..98a979f81 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java @@ -26,6 +26,9 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; + import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; @@ -50,6 +53,11 @@ public URI asUri() { return getValue(); } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return asUri().hashCode(); @@ -61,4 +69,13 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IAnyUriItem && compareTo((IAnyUriItem) obj) == 0); } + + private final class MapKey + extends AbstractStringMapKey { + + @Override + public IAnyUriItem getKey() { + return AbstractUriItem.this; + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java index fd6928109..bac8f6af9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.Base64Adapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.nio.ByteBuffer; @@ -51,6 +52,11 @@ public ByteBuffer asByteBuffer() { return getValue(); } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return asByteBuffer().hashCode(); @@ -62,4 +68,23 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IBase64BinaryItem && compareTo((IBase64BinaryItem) obj) == 0); } + + private final class MapKey implements IMapKey { + @Override + public IBase64BinaryItem getKey() { + return Base64BinaryItemImpl.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey())); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java index 9a8daf727..981f0d630 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import edu.umd.cs.findbugs.annotations.NonNull; @@ -77,6 +78,11 @@ public String toString() { return asString(); } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return Boolean.hashCode(booleanValue); @@ -88,4 +94,23 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IBooleanItem && compareTo((IBooleanItem) obj) == 0); } + + private final class MapKey implements IMapKey { + @Override + public IBooleanItem getKey() { + return BooleanItemImpl.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey())); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java index 67768d16d..0069771f0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.DayTimeAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.time.Duration; @@ -51,6 +52,11 @@ public DayTimeAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.DAY_TIME_DURATION; } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return asDuration().hashCode(); @@ -62,4 +68,23 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IDayTimeDurationItem && compareTo((IDayTimeDurationItem) obj) == 0); } + + private final class MapKey implements IMapKey { + @Override + public IDayTimeDurationItem getKey() { + return DayTimeDurationItemImpl.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey())); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java index 7e375c959..af4f04646 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java @@ -36,8 +36,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; class DecimalItemImpl - extends AbstractAnyAtomicItem - implements IDecimalItem { + extends AbstractDecimalItem { public DecimalItemImpl(@NonNull BigDecimal value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java index a915a9ce7..c905e4e90 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java @@ -27,14 +27,15 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.IStringValued; +import gov.nist.secauto.metaschema.core.metapath.IPrintable; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Set; import edu.umd.cs.findbugs.annotations.NonNull; -public interface IAnyAtomicItem extends IAtomicValuedItem, IStringValued { +public interface IAnyAtomicItem extends IAtomicValuedItem, IPrintable { @NonNull Set> PRIMITIVE_ITEM_TYPES = ObjectUtils.notNull(Set.of( IStringItem.class, @@ -74,6 +75,9 @@ default IAnyAtomicItem toAtomicItem() { @NonNull String asString(); + @NonNull + IMapKey asMapKey(); + /** * Get a new {@link IStringItem} based on the the textual value of the item's * "wrapped" value. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java index a84d82c78..e8907a9be 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java @@ -38,9 +38,6 @@ public interface IIPAddressItem extends IUntypedAtomicItem { @NonNull IPAddress asIpAddress(); - @Override - int compareTo(IAnyAtomicItem item); - /** * Compares this value with the argument. * diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java index b86d4108a..f4660650e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java @@ -33,7 +33,7 @@ import inet.ipaddr.ipv4.IPv4Address; class IPv4AddressItemImpl - extends AbstractAnyAtomicItem + extends AbstractUntypedAtomicItem implements IIPv4AddressItem { public IPv4AddressItemImpl(@NonNull IPv4Address value) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java index 838bd8f37..69b0c398d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java @@ -33,7 +33,7 @@ import inet.ipaddr.ipv6.IPv6Address; class IPv6AddressItemImpl - extends AbstractAnyAtomicItem + extends AbstractUntypedAtomicItem implements IIPv6AddressItem { public IPv6AddressItemImpl(@NonNull IPv6Address value) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java new file mode 100644 index 000000000..a93b308b6 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java @@ -0,0 +1,31 @@ +/* + * 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.item.atomic; + +public interface ITemporalItem extends IAnyAtomicItem { + boolean hasTimezone(); +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java index 1596e8d80..852dba9db 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java @@ -33,7 +33,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; class MarkupLineItemImpl - extends AbstractAnyAtomicItem + extends AbstractUntypedAtomicItem implements IMarkupItem { public MarkupLineItemImpl(@NonNull MarkupLine value) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java index f782a2241..9229d1cb3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java @@ -33,7 +33,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; class MarkupMultiLineItemImpl - extends AbstractAnyAtomicItem + extends AbstractUntypedAtomicItem implements IMarkupItem { public MarkupMultiLineItemImpl(@NonNull MarkupMultiline value) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java index 261837ec7..99ef11b7b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.UuidAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.util.UUID; @@ -50,4 +51,28 @@ public UUID asUuid() { public UuidAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.UUID; } + + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + + private final class MapKey implements IMapKey { + @Override + public IUuidItem getKey() { + return UuidItemImpl.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof MapKey + && getKey().asUuid().equals(((MapKey) obj).getKey().asUuid())); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java index 0719cad29..fa40dcfd5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.YearMonthAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.time.Period; import java.util.Objects; @@ -52,6 +53,11 @@ public YearMonthAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.YEAR_MONTH_DURATION; } + @Override + public IMapKey asMapKey() { + return new MapKey(); + } + @Override public int hashCode() { return Objects.hash(asPeriod()); @@ -63,4 +69,23 @@ public boolean equals(Object obj) { return this == obj || (obj instanceof IYearMonthDurationItem && compareTo((IYearMonthDurationItem) obj) == 0); } + + private final class MapKey implements IMapKey { + @Override + public IYearMonthDurationItem getKey() { + return YearMonthDurationItemImpl.this; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return this == obj || + (obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey())); + } + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java index 754d186e0..6f00bf4b8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItem.java @@ -28,8 +28,8 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.IPrintable; import gov.nist.secauto.metaschema.core.metapath.ISequence; -import gov.nist.secauto.metaschema.core.metapath.IStringValued; 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.function.ISequenceType; @@ -65,7 +65,7 @@ * the Metapath item type of array members */ @SuppressWarnings("PMD.ShortMethodName") -public interface IArrayItem extends IFunction, IItem, List, IStringValued { +public interface IArrayItem extends IFunction, IItem, List, IPrintable { @NonNull static IArrayItem empty() { return AbstractArrayItem.empty(); @@ -258,11 +258,10 @@ default Stream flatten() { .flatMap(ICollectionValue::flatten); } - @SuppressWarnings("unchecked") @NonNull static IArrayItem ofCollection( // NOPMD - intentional - @NonNull List items) { - return items.isEmpty() ? empty() : (IArrayItem) new ArrayItemN<>(items); + @NonNull List items) { + return items.isEmpty() ? empty() : new ArrayItemN<>(items); } /** @@ -559,7 +558,6 @@ static IArrayItem of(@NonNull T... items) { * {@code Collection} * @throws NullPointerException * if collection is null, or if it contains any nulls - * @since 10 */ @SuppressWarnings("unchecked") @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java new file mode 100644 index 000000000..af82e5acb --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapItem.java @@ -0,0 +1,728 @@ +/* + * 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.item.function; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.IPrintable; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +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.function.ISequenceType; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractArrayItem; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractMapItem; +import gov.nist.secauto.metaschema.core.metapath.impl.MapItemN; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; + +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * + * @param + * the key type + * @param + * the value type + */ +public interface IMapItem + extends IFunction, IItem, Map, IPrintable { + @NonNull + static IMapItem empty() { + return AbstractMapItem.empty(); + } + + @Override + default QName getQName() { + return AbstractArrayItem.QNAME; + } + + @Override + default Set getProperties() { + return AbstractArrayItem.PROPERTIES; + } + + @Override + default boolean isDeterministic() { + return true; + } + + @Override + default boolean isContextDepenent() { + return false; + } + + @Override + default boolean isFocusDepenent() { + return false; + } + + @Override + default List getArguments() { + return AbstractArrayItem.ARGUMENTS; + } + + @Override + default int arity() { + return 1; + } + + @Override + default boolean isArityUnbounded() { + return false; + } + + @Override + default ISequenceType getResult() { + return AbstractArrayItem.RESULT; + } + + @Override + ISequence execute(List> arguments, DynamicContext dynamicContext, ISequence focus); + + @Override + default String toSignature() { + return "array()"; + } + + @Override + Map getValue(); + + @Override + default boolean hasValue() { + return true; + } + + /** + * Determine if this sequence is empty. + * + * @return {@code true} if the sequence contains no items, or {@code false} + * otherwise + */ + @Override + default boolean isEmpty() { + return getValue().isEmpty(); + } + + /** + * Get the count of items in this sequence. + * + * @return the count of items + */ + @Override + default int size() { + return getValue().size(); + + } + + @Override + default ISequence> asSequence() { + return ISequence.of(this); + } + + @NonNull + static IMapItem ofCollection( // NOPMD - intentional + @NonNull Map map) { + return map.isEmpty() ? empty() : new MapItemN<>(map); + } + + /** + * Returns an unmodifiable map containing zero mappings. See + * Unmodifiable Maps for details. + * + * @param + * the key type + * @param + * the value type + * @return an empty {@code IMapItem} + * + * @since 9 + */ + static IMapItem of() { + return AbstractMapItem.empty(); + } + + /** + * Returns an unmodifiable map containing a single mapping. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the mapping's key + * @param v1 + * the mapping's value + * @return a {@code Map} containing the specified mapping + * @throws NullPointerException + * if the key or the value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1) { + return new MapItemN<>(entry(k1, v1)); + } + + /** + * Returns an unmodifiable map containing two mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if the keys are duplicates + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2)); + } + + /** + * Returns an unmodifiable map containing three mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static + IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3)); + } + + /** + * Returns an unmodifiable map containing four mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static + IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4)); + } + + /** + * Returns an unmodifiable map containing five mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, + V v4, K k5, V v5) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5)); + } + + /** + * Returns an unmodifiable map containing six mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @param k6 + * the sixth mapping's key + * @param v6 + * the sixth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, + V v4, K k5, V v5, K k6, V v6) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6)); + } + + /** + * Returns an unmodifiable map containing seven mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @param k6 + * the sixth mapping's key + * @param v6 + * the sixth mapping's value + * @param k7 + * the seventh mapping's key + * @param v7 + * the seventh mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, + V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6), + entry(k7, v7)); + } + + /** + * Returns an unmodifiable map containing eight mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @param k6 + * the sixth mapping's key + * @param v6 + * the sixth mapping's value + * @param k7 + * the seventh mapping's key + * @param v7 + * the seventh mapping's value + * @param k8 + * the eighth mapping's key + * @param v8 + * the eighth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, + V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6), + entry(k7, v7), entry(k8, v8)); + } + + /** + * Returns an unmodifiable map containing nine mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @param k6 + * the sixth mapping's key + * @param v6 + * the sixth mapping's value + * @param k7 + * the seventh mapping's key + * @param v7 + * the seventh mapping's value + * @param k8 + * the eighth mapping's key + * @param v8 + * the eighth mapping's value + * @param k9 + * the ninth mapping's key + * @param v9 + * the ninth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, + V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6), + entry(k7, v7), entry(k8, v8), entry(k9, v9)); + } + + /** + * Returns an unmodifiable map containing ten mappings. See + * Unmodifiable Maps for details. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param k1 + * the first mapping's key + * @param v1 + * the first mapping's value + * @param k2 + * the second mapping's key + * @param v2 + * the second mapping's value + * @param k3 + * the third mapping's key + * @param v3 + * the third mapping's value + * @param k4 + * the fourth mapping's key + * @param v4 + * the fourth mapping's value + * @param k5 + * the fifth mapping's key + * @param v5 + * the fifth mapping's value + * @param k6 + * the sixth mapping's key + * @param v6 + * the sixth mapping's value + * @param k7 + * the seventh mapping's key + * @param v7 + * the seventh mapping's value + * @param k8 + * the eighth mapping's key + * @param v8 + * the eighth mapping's value + * @param k9 + * the ninth mapping's key + * @param v9 + * the ninth mapping's value + * @param k10 + * the tenth mapping's key + * @param v10 + * the tenth mapping's value + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any key or value is {@code null} + * + * @since 9 + */ + static IMapItem of(K k1, V v1, K k2, V v2, K k3, V v3, + K k4, + V v4, K k5, V v5, + K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) { + return new MapItemN<>(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4), entry(k5, v5), entry(k6, v6), + entry(k7, v7), entry(k8, v8), entry(k9, v9), entry(k10, v10)); + } + + /** + * Returns an unmodifiable map containing keys and values extracted from the + * given entries. The entries themselves are not stored in the map. See + * Unmodifiable Maps for details. + * + * @apiNote It is convenient to create the map entries using the + * {@link Map#entry Map.entry()} method. For example, + * + *
{@code
+   *     import static java.util.Map.entry;
+   *
+   *     Map map = Map.ofEntries(
+   *         entry(1, "a"),
+   *         entry(2, "b"),
+   *         entry(3, "c"),
+   *         ...
+   *         entry(26, "z"));
+   * }
+ * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param entries + * {@code Map.Entry}s containing the keys and values from which the map + * is populated + * @return a {@code Map} containing the specified mappings + * @throws IllegalArgumentException + * if there are any duplicate keys + * @throws NullPointerException + * if any entry, key, or value is {@code null}, or if the + * {@code entries} array is {@code null} + * + * @see Map#entry Map.entry() + * @since 9 + */ + @SafeVarargs + @SuppressWarnings("varargs") + static + IMapItem ofEntries(Map.Entry... entries) { + return entries.length == 0 ? empty() : new MapItemN<>(entries); + } + + /** + * Returns an unmodifiable {@link Entry} containing the given key and value. + * These entries are suitable for populating {@code Map} instances using the + * {@link Map#ofEntries Map.ofEntries()} method. The {@code Entry} instances + * created by this method have the following characteristics: + * + *
    + *
  • They disallow {@code null} keys and values. Attempts to create them using + * a {@code null} key or value result in {@code NullPointerException}. + *
  • They are unmodifiable. Calls to {@link Entry#setValue Entry.setValue()} + * on a returned {@code Entry} result in {@code UnsupportedOperationException}. + *
  • They are not serializable. + *
  • They are value-based. + * Callers should make no assumptions about the identity of the returned + * instances. This method is free to create new instances or reuse existing + * ones. Therefore, identity-sensitive operations on these instances (reference + * equality ({@code ==}), identity hash code, and synchronization) are + * unreliable and should be avoided. + *
+ * + * @apiNote For a serializable {@code Entry}, see + * {@link AbstractMap.SimpleEntry} or + * {@link AbstractMap.SimpleImmutableEntry}. + * + * @param + * the key's type + * @param + * the value's type + * @param k + * the key + * @param v + * the value + * @return an {@code Entry} containing the specified key and value + * @throws NullPointerException + * if the key or value is {@code null} + * + * @see Map#ofEntries Map.ofEntries() + * @since 9 + */ + + static Map.Entry entry(IAnyAtomicItem k, V v) { + return entry(k.asMapKey(), v); + } + + static Map.Entry entry(IMapKey k, V v) { + return Map.entry(k, v); + } + + /** + * Returns an unmodifiable Map containing the + * entries of the given Map. The given Map must not be null, and it must not + * contain any null keys or values. If the given Map is subsequently modified, + * the returned Map will not reflect such modifications. + * + * @implNote If the given Map is an unmodifiable + * Map, calling copyOf will generally not create a copy. + * + * @param + * the {@code Map}'s key type + * @param + * the {@code Map}'s value type + * @param map + * a {@code Map} from which entries are drawn, must be non-null + * @return a {@code Map} containing the entries of the given {@code Map} + * @throws NullPointerException + * if map is null, or if it contains any null keys or values + */ + @SuppressWarnings("unchecked") + static + IMapItem copyOf(Map map) { + return map instanceof IMapItem + ? (IMapItem) map + : map.isEmpty() + ? empty() + : new MapItemN<>(new LinkedHashMap<>(map)); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapKey.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapKey.java new file mode 100644 index 000000000..33ad72909 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/function/IMapKey.java @@ -0,0 +1,43 @@ +/* + * 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.item.function; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public interface IMapKey { + + @NonNull + IAnyAtomicItem getKey(); + + @Override + int hashCode(); + + @Override + boolean equals(Object obj); +} diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index 69c153471..d79ae1575 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -1,4 +1,3 @@ - /* * 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 @@ -25,6 +24,33 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ +/* + * 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. + */ + import gov.nist.secauto.metaschema.core.datatype.IDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupDataTypeProvider; @@ -87,6 +113,7 @@ exports gov.nist.secauto.metaschema.core.metapath.function.library; exports gov.nist.secauto.metaschema.core.metapath.item; exports gov.nist.secauto.metaschema.core.metapath.item.atomic; + exports gov.nist.secauto.metaschema.core.metapath.item.function; exports gov.nist.secauto.metaschema.core.metapath.item.node; exports gov.nist.secauto.metaschema.core.model; exports gov.nist.secauto.metaschema.core.model.constraint; diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGetTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGetTest.java new file mode 100644 index 000000000..256024743 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGetTest.java @@ -0,0 +1,66 @@ +/* + * 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 static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ExpressionTestBase; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; + +import org.junit.jupiter.api.Test; + +import javax.xml.namespace.QName; + +class MapGetTest + extends ExpressionTestBase { + + @Test + void test() { + IMapItem week = IMapItem.of( + integer(0), string("Sonntag"), + integer(1), string("Montag"), + integer(2), string("Dienstag"), + integer(3), string("Mittwoch"), + integer(4), string("Donnerstag"), + integer(5), string("Freitag"), + integer(6), string("Samstag")); + + DynamicContext context = new DynamicContext(); + context.bindVariableValue(new QName("week"), ISequence.of(week)); + + IItem result = MetapathExpression.compile("map:get($week, 4)") + .evaluateAs(null, MetapathExpression.ResultType.NODE, context); + assertEquals(string("Donnerstag"), result); + } + +} diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItemTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItemTest.java index a83500140..3a4531648 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItemTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/IArrayItemTest.java @@ -31,14 +31,11 @@ import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; 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.ExpressionTestBase; import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; -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; @@ -120,48 +117,4 @@ void testFunctionCallLookup(@NonNull ISequence expected, @NonNull String meta assertEquals(expected, result); } - private static Stream postfixLookupValues() { // NOPMD - false positive - return Stream.of( - // postfix lookup - // Arguments.of( - // sequence(string("Jenna")), - // "map { \"first\" : \"Jenna\", \"last\" : \"Scott\" }?first"), - Arguments.of( - sequence(integer(5)), - "[4, 5, 6]?2"), - // Arguments.of( - // sequence(string("Tom"), string("Dick"), string("Harry")), - // "(map {\"first\": \"Tom\"}, map {\"first\": \"Dick\"}, map {\"first\": - // \"Harry\"})?first"), - Arguments.of( - sequence(integer(2), integer(5)), - "([1,2,3], [4,5,6])?2"), - Arguments.of( - sequence(integer(1), integer(2), integer(5), integer(7)), - "[1, 2, 5, 7]?*"), - Arguments.of( - sequence(array(integer(1), integer(2), integer(3)), array(integer(4), integer(5), integer(6))), - "[[1, 2, 3], [4, 5, 6]]?*")); - } - - @ParameterizedTest - @MethodSource("postfixLookupValues") - void testPostfixLookup(@NonNull ISequence expected, @NonNull String metapath) { - ISequence result = MetapathExpression.compile(metapath) - .evaluateAs(null, MetapathExpression.ResultType.SEQUENCE, newDynamicContext()); - assertEquals(expected, result); - } - - @Test - void testUnaryLookupMissingMember() { - ArrayException thrown = assertThrows( - ArrayException.class, - () -> { - ISequence result = MetapathExpression.compile("([1,2,3], [1,2,5], [1,2])[?3 = 5]") - .evaluateAs(null, MetapathExpression.ResultType.SEQUENCE, newDynamicContext()); - assertNotNull(result); - result.safeStream(); - }); - assertEquals(ArrayException.INDEX_OUT_OF_BOUNDS, thrown.getCode()); - } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java new file mode 100644 index 000000000..168ba7ba0 --- /dev/null +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/item/function/LookupTest.java @@ -0,0 +1,102 @@ +/* + * 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.item.function; + +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.array; +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.sequence; +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.string; +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.ExpressionTestBase; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; + +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; + +public class LookupTest + extends ExpressionTestBase { + + private static Stream postfixLookupValues() { // NOPMD - false positive + return Stream.of( + // postfix lookup + Arguments.of( + sequence(string("Jenna")), + "map { \"first\" : \"Jenna\", \"last\" : \"Scott\" }?first"), + Arguments.of( + sequence(integer(5)), + "[4, 5, 6]?2"), + Arguments.of( + sequence(string("Tom"), string("Dick"), string("Harry")), + "(map {\"first\": \"Tom\"}, map {\"first\": \"Dick\"}, map {\"first\":\"Harry\"})?first"), + Arguments.of( + sequence(string("Donnerstag")), + "let $week := map{0:\"Sonntag\", 1:\"Montag\", 2:\"Dienstag\", 3:\"Mittwoch\", 4:\"Donnerstag\", 5:\"Freitag\", 6:\"Samstag\"} return $week?4"), + Arguments.of( + sequence(string("Donnerstag")), + "let $week := map{0:\"Sonntag\", 1:\"Montag\", 2:\"Dienstag\", 3:\"Mittwoch\", 4:\"Donnerstag\", 5:\"Freitag\", 6:\"Samstag\"} return $week(4)"), + Arguments.of( + sequence(integer(2), integer(5)), + "([1,2,3], [4,5,6])?2"), + Arguments.of( + sequence(integer(1), integer(2), integer(5), integer(7)), + "[1, 2, 5, 7]?*"), + Arguments.of( + sequence(array(integer(1), integer(2), integer(3)), array(integer(4), integer(5), integer(6))), + "[[1, 2, 3], [4, 5, 6]]?*")); + } + + @ParameterizedTest + @MethodSource("postfixLookupValues") + void testPostfixLookup(@NonNull ISequence expected, @NonNull String metapath) { + ISequence result = MetapathExpression.compile(metapath) + .evaluateAs(null, MetapathExpression.ResultType.SEQUENCE, newDynamicContext()); + assertEquals(expected, result); + } + + @Test + void testUnaryLookupMissingMember() { + ArrayException thrown = assertThrows( + ArrayException.class, + () -> { + ISequence result = MetapathExpression.compile("([1,2,3], [1,2,5], [1,2])[?3 = 5]") + .evaluateAs(null, MetapathExpression.ResultType.SEQUENCE, newDynamicContext()); + assertNotNull(result); + result.safeStream(); + }); + assertEquals(ArrayException.INDEX_OUT_OF_BOUNDS, thrown.getCode()); + } +} diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/NullJavaTypeAdapter.java b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/NullJavaTypeAdapter.java index a140aadca..e29dfad73 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/NullJavaTypeAdapter.java +++ b/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/annotations/NullJavaTypeAdapter.java @@ -31,6 +31,7 @@ import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import gov.nist.secauto.metaschema.databind.model.annotations.NullJavaTypeAdapter.VoidItem; import java.util.List; @@ -119,5 +120,10 @@ public IAnyAtomicItem castAsType(IAnyAtomicItem item) { public int compareTo(IAnyAtomicItem item) { throw new UnsupportedOperationException(NOT_VALID); } + + @Override + public IMapKey asMapKey() { + throw new UnsupportedOperationException(NOT_VALID); + } } }