diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6d826dcd1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# To list all file extensions: +# git ls-files | awk -F . {'print $NF'}|sort -u +# +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# files that are binary +*.png +*.jpg diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba26e1aa1..f6b59f9af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,18 @@ on: tags: - "v*" workflow_dispatch: + inputs: + release: + description: 'Delpoy release?' + type: boolean + default: false + website: + description: 'Deploy website?' + type: boolean + default: false + pages-branch: + description: 'Pages branch name' + default: nist-pages name: Deploy Tagged Release jobs: deploy-to-nexus: @@ -38,6 +50,7 @@ jobs: # Maven Deploy # ------------------------- - name: Deploy Maven Artifacts + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && contains('true', github.event.inputs.release)) run: | mvn -B -e -Pgpg -Prelease -Preporting deploy # mvn -Pgpg -Prelease nexus-staging:close -DstagingDescription="closing to release" @@ -46,6 +59,7 @@ jobs: MAVEN_CENTRAL_TOKEN: ${{ secrets.SONATYPE_PASSWORD }} MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Create release + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && contains('true', github.event.inputs.release)) uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 with: draft: true @@ -57,9 +71,14 @@ jobs: # Maven Site # ------------------------- - name: Build Website + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && contains('true', github.event.inputs.website)) run: | mvn -B -e -Prelease -Preporting install site site:stage - - name: Run Website Deploy Script - run: | - touch target/staging/.nojekyll - bash .github/workflows/deploy.sh --push-only -v -m "Deploying website [ci skip]" + - name: Website Deploy + uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && contains('true', github.event.inputs.website)) + with: + personal_token: ${{ secrets.COMMIT_TOKEN }} + publish_dir: ./target/staging + external_repository: ${{ github.repository }} + publish_branch: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.pages-branch) || 'nist-pages' }} diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/AbstractExitStatus.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/AbstractExitStatus.java index 46d0fd85c..c099e2c69 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/AbstractExitStatus.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/AbstractExitStatus.java @@ -106,10 +106,10 @@ public void generateMessage(boolean showStackTrace) { if (message != null && !message.isEmpty()) { logBuilder.log(message); - } else if (throwable != null && showStackTrace) { + } else if (showStackTrace && throwable != null) { // log the throwable logBuilder.log(); - } + } // otherwise there is nothing to log } } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java index 44a921fdd..042c260e0 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/CLIProcessor.java @@ -66,6 +66,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class CLIProcessor { private static final Logger LOGGER = LogManager.getLogger(CLIProcessor.class); @@ -109,12 +110,14 @@ public class CLIProcessor { SHOW_STACK_TRACE_OPTION, VERSION_OPTION); + public static final String COMMAND_VERSION = "http://csrc.nist.gov/ns/metaschema-java/cli/command-version"; + @NonNull private final List commands = new LinkedList<>(); @NonNull private final String exec; @NonNull - private final List versionInfos; + private final Map versionInfos; public static void main(String... args) { System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); @@ -129,10 +132,10 @@ public static void main(String... args) { @SuppressWarnings("null") public CLIProcessor(@NonNull String exec) { - this(exec, List.of()); + this(exec, Map.of()); } - public CLIProcessor(@NonNull String exec, @NonNull List versionInfos) { + public CLIProcessor(@NonNull String exec, @NonNull Map versionInfos) { this.exec = exec; this.versionInfos = versionInfos; AnsiConsole.systemInstall(); @@ -154,7 +157,7 @@ public String getExec() { * @return the versionInfo */ @NonNull - public List getVersionInfos() { + public Map getVersionInfos() { return versionInfos; } @@ -195,7 +198,7 @@ private ExitStatus parseCommand(String... args) { return status; } - protected List getTopLevelCommands() { + protected final List getTopLevelCommands() { List retval = Collections.unmodifiableList(commands); assert retval != null; return retval; @@ -206,7 +209,6 @@ private static void handleNoColor() { AnsiConsole.systemUninstall(); } - @SuppressWarnings("resource") public static void handleQuiet() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); // NOPMD not closable here Configuration config = ctx.getConfiguration(); @@ -220,7 +222,7 @@ public static void handleQuiet() { protected void showVersion() { @SuppressWarnings("resource") PrintStream out = AnsiConsole.out(); // NOPMD - not owner - getVersionInfos().stream().forEach((info) -> { + getVersionInfos().values().stream().forEach(info -> { out.println(ansi() .bold().a(info.getName()).boldOff() .a(" ") @@ -253,6 +255,7 @@ public class CallingContext { @NonNull private final List extraArgs; + @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields") public CallingContext(@NonNull List args) { Map topLevelCommandMap = getTopLevelCommands().stream() .collect(Collectors.toUnmodifiableMap(ICommand::getName, Function.identity())); @@ -263,35 +266,31 @@ public CallingContext(@NonNull List args) { boolean endArgs = false; for (String arg : args) { - if (endArgs) { + if (endArgs || arg.startsWith("-")) { extraArgs.add(arg); + } else if ("--".equals(arg)) { + endArgs = true; } else { - if (arg.startsWith("-")) { + ICommand command; + if (calledCommands.isEmpty()) { + command = topLevelCommandMap.get(arg); + } else { + command = calledCommands.getLast(); + command = command.getSubCommandByName(arg); + } + + if (command == null) { extraArgs.add(arg); - } else if ("--".equals(arg)) { endArgs = true; } else { - ICommand command; - if (calledCommands.isEmpty()) { - command = topLevelCommandMap.get(arg); - } else { - command = calledCommands.getLast(); - command = command.getSubCommandByName(arg); - } - - if (command == null) { - extraArgs.add(arg); - endArgs = true; - } else { - calledCommands.add(command); - } + calledCommands.add(command); } } } if (LOGGER.isDebugEnabled()) { String commandChain = calledCommands.stream() - .map(command -> command.getName()) + .map(ICommand::getName) .collect(Collectors.joining(" -> ")); LOGGER.debug("Processing command chain: {}", commandChain); } @@ -311,6 +310,11 @@ public CallingContext(@NonNull List args) { this.extraArgs = extraArgs; } + @NonNull + public CLIProcessor getCLIProcessor() { + return CLIProcessor.this; + } + @Nullable public ICommand getTargetCommand() { return calledCommands.peekLast(); @@ -546,7 +550,7 @@ protected String buildHelpCliSyntax() { // output required options getOptionsList().stream() - .filter(option -> option.isRequired()) + .filter(Option::isRequired) .forEach(option -> { builder .append(' ') diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractParentCommand.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractParentCommand.java index dc3f72b9a..0416006e3 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractParentCommand.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractParentCommand.java @@ -51,7 +51,7 @@ protected AbstractParentCommand(boolean subCommandRequired) { this.subCommandRequired = subCommandRequired; } - protected void addCommandHandler(ICommand handler) { + protected final void addCommandHandler(ICommand handler) { String commandName = handler.getName(); this.commandToSubcommandHandlerMap.put(commandName, handler); } @@ -87,7 +87,7 @@ protected ExitStatus executeCommand( status = ExitCode.INVALID_COMMAND .exitMessage("Please use one of the following sub-commands: " + getSubCommands().stream() - .map(command -> command.getName()) + .map(ICommand::getName) .collect(Collectors.joining(", "))); } else { status = ExitCode.OK.exit(); diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/DefaultExtraArgument.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/DefaultExtraArgument.java index d98b79211..591ed48f1 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/DefaultExtraArgument.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/DefaultExtraArgument.java @@ -26,15 +26,19 @@ package gov.nist.secauto.metaschema.cli.processor.command; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + public class DefaultExtraArgument implements ExtraArgument { private final String name; private final boolean required; private final int number; + @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields") public DefaultExtraArgument(String name, boolean required) { this(name, required, 1); } + @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields") public DefaultExtraArgument(String name, boolean required, int number) { if (number < 1) { throw new IllegalArgumentException("number must be a positive value"); diff --git a/cli-processor/src/main/resources/log4j2.xml b/cli-processor/src/main/resources/log4j2.xml index b174b3f80..758bf2a4c 100644 --- a/cli-processor/src/main/resources/log4j2.xml +++ b/cli-processor/src/main/resources/log4j2.xml @@ -2,11 +2,11 @@ - + - + diff --git a/core/metaschema b/core/metaschema index 16919d512..088bda7f6 160000 --- a/core/metaschema +++ b/core/metaschema @@ -1 +1 @@ -Subproject commit 16919d512f96110c49fa2edcd3e6ee9a377c0b69 +Subproject commit 088bda7f6fbad8ad0132f8fdb30b3d81c7b578ab diff --git a/core/pom.xml b/core/pom.xml index 6e8baf269..e4fabc367 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -169,9 +169,10 @@ jacoco-maven-plugin - - gov/nist/secauto/metaschema/core/model/xml/xmlbeans/**/* + + gov/nist/secauto/metaschema/core/model/xml/xmlbeans/**/* org/apache/xmlbeans/**/* + gov/nist/secauto/metaschema/core/metapath/antlr/* diff --git a/core/src/main/antlr4/Metapath10.g4 b/core/src/main/antlr4/Metapath10.g4 index 77e81a4b0..0fb07f3ca 100644 --- a/core/src/main/antlr4/Metapath10.g4 +++ b/core/src/main/antlr4/Metapath10.g4 @@ -4,16 +4,13 @@ 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 ; // [5] -// enclosedexpr : OC expr? CC ; +enclosedexpr : OC expr? CC ; expr : exprsingle ( COMMA exprsingle)* ; exprsingle : forexpr | letexpr | quantifiedexpr | ifexpr | orexpr ; forexpr : simpleforclause KW_RETURN exprsingle ; @@ -57,14 +54,11 @@ relativepathexpr : stepexpr (( SLASH | SS) stepexpr)* ; stepexpr : postfixexpr | axisstep ; axisstep : (reversestep | forwardstep) predicatelist ; // [40] -// forwardstep : forwardaxis nodetest | abbrevforwardstep ; -forwardstep : forwardaxis nametest | abbrevforwardstep ; +forwardstep : forwardaxis nodetest | abbrevforwardstep ; // forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_ATTRIBUTE COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON | KW_FOLLOWING_SIBLING COLONCOLON | KW_FOLLOWING COLONCOLON | KW_NAMESPACE COLONCOLON ; forwardaxis : KW_CHILD COLONCOLON | KW_DESCENDANT COLONCOLON | KW_SELF COLONCOLON | KW_DESCENDANT_OR_SELF COLONCOLON ; -// abbrevforwardstep : AT? nodetest ; -abbrevforwardstep : AT? nametest ; -// reversestep : reverseaxis nodetest | abbrevreversestep ; -reversestep : reverseaxis nametest | abbrevreversestep ; +abbrevforwardstep : AT? nodetest ; +reversestep : reverseaxis nodetest | abbrevreversestep ; // reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_PRECEDING_SIBLING COLONCOLON | KW_PRECEDING COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ; reverseaxis : KW_PARENT COLONCOLON | KW_ANCESTOR COLONCOLON | KW_ANCESTOR_OR_SELF COLONCOLON ; // [45] @@ -73,19 +67,18 @@ abbrevreversestep : DD ; nodetest : nametest ; nametest : eqname | wildcard ; wildcard : STAR | NCName CS | SC NCName | BracedURILiteral STAR ; -// postfixexpr : primaryexpr (predicate | argumentlist | lookup)* ; -postfixexpr : primaryexpr (predicate)* ; +postfixexpr : primaryexpr (predicate | argumentlist | lookup)* ; // [50] argumentlist : OP (argument ( COMMA argument)*)? CP ; predicatelist : predicate* ; predicate : OB expr CB ; -// lookup : QM keyspecifier ; -// keyspecifier : NCName | IntegerLiteral | parenthesizedexpr | STAR ; +lookup : QM keyspecifier ; +keyspecifier : NCName | IntegerLiteral | parenthesizedexpr | STAR ; // [55] //arrowfunctionspecifier : eqname | varref | parenthesizedexpr ; arrowfunctionspecifier : eqname; // primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall | functionitemexpr | mapconstructor | arrayconstructor | unarylookup ; -primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall ; +primaryexpr : literal | varref | parenthesizedexpr | contextitemexpr | functioncall | mapconstructor | arrayconstructor | unarylookup; literal : numericliteral | StringLiteral ; numericliteral : IntegerLiteral | DecimalLiteral | DoubleLiteral ; varref : DOLLAR varname ; @@ -101,16 +94,16 @@ 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 ; -// arrayconstructor : squarearrayconstructor | curlyarrayconstructor ; -// squarearrayconstructor : OB (exprsingle ( COMMA exprsingle)*)? CB ; +mapconstructorentry : mapkeyexpr COLON mapvalueexpr ; +mapkeyexpr : exprsingle ; +mapvalueexpr : exprsingle ; +arrayconstructor : squarearrayconstructor | curlyarrayconstructor ; +squarearrayconstructor : OB (exprsingle ( COMMA exprsingle)*)? CB ; // [75] -// curlyarrayconstructor : KW_ARRAY enclosedexpr ; -// unarylookup : QM keyspecifier ; +curlyarrayconstructor : KW_ARRAY enclosedexpr ; +unarylookup : QM keyspecifier ; // singletype : simpletypename QM? ; // typedeclaration : KW_AS sequencetype ; // sequencetype : KW_EMPTY_SEQUENCE OP CP | itemtype occurrenceindicator? ; @@ -156,7 +149,7 @@ argument : exprsingle ; // 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/datatype/AbstractCustomJavaDataType.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataType.java index e5ad1b43a..d42b85329 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataType.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataType.java @@ -1,87 +1,87 @@ -/* - * 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.datatype; - -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.Objects; - -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * A common base implementation of a custom Java object providing an underlying - * implementation of a data type. - * - * @param - * the bound object type supported by this data type - * @param - * the inner value of the data type object - */ -public abstract class AbstractCustomJavaDataType, VALUE> - implements ICustomJavaDataType { - @NonNull - private final VALUE value; - - /** - * Construct a new instance of a custom Java object-based data value. - * - * @param value - * the bound object that the data type is based on - */ - protected AbstractCustomJavaDataType(@NonNull VALUE value) { - this.value = ObjectUtils.requireNonNull(value, "value"); - } - - /** - * Get the bound Java object value. - * - * @return the bound object - */ - @NonNull - public VALUE getValue() { - return value; - } - // - // public void setValue(T value) { - // this.value = value; - // } - - @Override - public int hashCode() { - return value.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return Objects.equals(value, obj); - } - - @Override - public String toString() { - return value.toString(); - } -} +/* + * 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.datatype; + +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Objects; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A common base implementation of a custom Java object providing an underlying + * implementation of a data type. + * + * @param + * the bound object type supported by this data type + * @param + * the inner value of the data type object + */ +public abstract class AbstractCustomJavaDataType, VALUE> + implements ICustomJavaDataType { + @NonNull + private final VALUE value; + + /** + * Construct a new instance of a custom Java object-based data value. + * + * @param value + * the bound object that the data type is based on + */ + protected AbstractCustomJavaDataType(@NonNull VALUE value) { + this.value = ObjectUtils.requireNonNull(value, "value"); + } + + /** + * Get the bound Java object value. + * + * @return the bound object + */ + @NonNull + public VALUE getValue() { + return value; + } + // + // public void setValue(T value) { + // this.value = value; + // } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return Objects.equals(value, obj); + } + + @Override + public String toString() { + return value.toString(); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataTypeAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataTypeAdapter.java index 9cad33bc4..8908f6d36 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataTypeAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractCustomJavaDataTypeAdapter.java @@ -1,68 +1,68 @@ -/* - * 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.datatype; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; - -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * Provides a Java type adapter implementation for data types that are based on - * {@link ICustomJavaDataType}. - * - * @param - * the Java type this adapter supports, which is based on - * {@link ICustomJavaDataType} - * @param - * the Metapath item type associated with the adapter - */ -public abstract class AbstractCustomJavaDataTypeAdapter< - TYPE extends ICustomJavaDataType< - TYPE>, - ITEM_TYPE extends IAnyAtomicItem> - extends AbstractDataTypeAdapter { - - /** - * Construct a new Java type adapter for the class based on - * {@link ICustomJavaDataType}. - * - * @param clazz - * a data type class based on {@link ICustomJavaDataType} - */ - public AbstractCustomJavaDataTypeAdapter(@NonNull Class clazz) { - super(clazz); - } - - @SuppressWarnings("unchecked") - @Override - public TYPE copy(Object obj) { - // Datatype-based types are required to provide a copy method. Delegate to this - // method. - return ((TYPE) obj).copy(); - } - -} +/* + * 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.datatype; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Provides a Java type adapter implementation for data types that are based on + * {@link ICustomJavaDataType}. + * + * @param + * the Java type this adapter supports, which is based on + * {@link ICustomJavaDataType} + * @param + * the Metapath item type associated with the adapter + */ +public abstract class AbstractCustomJavaDataTypeAdapter< + TYPE extends ICustomJavaDataType< + TYPE>, + ITEM_TYPE extends IAnyAtomicItem> + extends AbstractDataTypeAdapter { + + /** + * Construct a new Java type adapter for the class based on + * {@link ICustomJavaDataType}. + * + * @param clazz + * a data type class based on {@link ICustomJavaDataType} + */ + public AbstractCustomJavaDataTypeAdapter(@NonNull Class clazz) { + super(clazz); + } + + @SuppressWarnings("unchecked") + @Override + public TYPE copy(Object obj) { + // Datatype-based types are required to provide a copy method. Delegate to this + // method. + return ((TYPE) obj).copy(); + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeProvider.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeProvider.java index 256bb755a..9fac25c1a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeProvider.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeProvider.java @@ -61,7 +61,7 @@ public List> getJavaTypeAdapters() { * if another type adapter has no name */ protected void registerDatatype(@NonNull IDataTypeAdapter adapter) { - if (adapter.getNames().size() == 0) { + if (adapter.getNames().isEmpty()) { throw new IllegalArgumentException("The adapter has no name: " + adapter.getClass().getName()); } synchronized (this) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/DataTypeService.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/DataTypeService.java index 84db86536..0c705ad0f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/DataTypeService.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/DataTypeService.java @@ -39,6 +39,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import nl.talsmasoftware.lazy4j.Lazy; @@ -51,8 +53,9 @@ public final class DataTypeService { private static final Logger LOGGER = LogManager.getLogger(DataTypeService.class); private static final Lazy INSTANCE = Lazy.lazy(() -> new DataTypeService()); - private Map> libraryByName; - private Map>, IDataTypeAdapter> libraryByClass; + private final Map> typeByName; + private final Map> typeByQName; + private final Map>, IDataTypeAdapter> typeByClass; /** * Get the singleton service instance, which will be lazy constructed on first @@ -67,57 +70,36 @@ public static DataTypeService getInstance() { } private DataTypeService() { - load(); - } - - /** - * Lookup a specific {@link IDataTypeAdapter} instance by its name. - * - * @param name - * the data type name of data type adapter to get the instance for - * @return the instance or {@code null} if the instance is unknown to the type - * system - */ - @Nullable - public IDataTypeAdapter getJavaTypeAdapterByName(@NonNull String name) { - return libraryByName.get(name); - } - - /** - * Lookup a specific {@link IDataTypeAdapter} instance by its class. - * - * @param clazz - * the adapter class to get the instance for - * @param - * the type of the requested adapter - * @return the instance or {@code null} if the instance is unknown to the type - * system - */ - @SuppressWarnings("unchecked") - @Nullable - public > TYPE getJavaTypeAdapterByClass(@NonNull Class clazz) { - return (TYPE) libraryByClass.get(clazz); - } - /** - * Load available data types registered with the {@link IDataTypeProvider} SPI. - * - * @throws IllegalStateException - * if there are two adapters with the same name - */ - private void load() { ServiceLoader loader = ServiceLoader.load(IDataTypeProvider.class); List> dataTypes = loader.stream() .map(Provider::get) .flatMap(provider -> provider.getJavaTypeAdapters().stream()) .collect(Collectors.toList()); - Map> libraryByName = dataTypes.stream() + Map> typeByName = dataTypes.stream() .flatMap(dataType -> dataType.getNames().stream() - .map(name -> Map.entry(name, dataType))) + .map(qname -> Map.entry(qname.getLocalPart(), dataType))) .collect(CustomCollectors.toMap( Map.Entry::getKey, - (entry) -> entry.getValue(), + Map.Entry::getValue, + (key, v1, v2) -> { + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Data types '{}' and '{}' have duplicate name '{}'. Using the first.", + v1.getClass().getName(), + v2.getClass().getName(), + key); + } + return v1; + }, + ConcurrentHashMap::new)); + + Map> typeByQName = dataTypes.stream() + .flatMap(dataType -> dataType.getNames().stream() + .map(qname -> Map.entry(qname, dataType))) + .collect(CustomCollectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, (key, v1, v2) -> { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Data types '{}' and '{}' have duplicate name '{}'. Using the first.", @@ -130,7 +112,7 @@ private void load() { ConcurrentHashMap::new)); @SuppressWarnings({ "unchecked", "null" }) Map>, - IDataTypeAdapter> libraryByClass = dataTypes.stream() + IDataTypeAdapter> typeByClass = dataTypes.stream() .collect(CustomCollectors.toMap( dataType -> (Class>) dataType.getClass(), Function.identity(), @@ -142,10 +124,50 @@ private void load() { return v1; }, ConcurrentHashMap::new)); + this.typeByName = typeByName; + this.typeByQName = typeByQName; + this.typeByClass = typeByClass; + } + + /** + * Lookup a specific {@link IDataTypeAdapter} instance by its name. + * + * @param qname + * the qualified name of data type adapter to get the instance for + * @return the instance or {@code null} if the instance is unknown to the type + * system + */ + @Nullable + public IDataTypeAdapter getJavaTypeAdapterByQName(@NonNull QName qname) { + return typeByQName.get(qname); + } - synchronized (this) { - this.libraryByName = libraryByName; - this.libraryByClass = libraryByClass; - } + /** + * Lookup a specific {@link IDataTypeAdapter} instance by its name. + * + * @param name + * the name of data type adapter to get the instance for + * @return the instance or {@code null} if the instance is unknown to the type + * system + */ + @Nullable + public IDataTypeAdapter getJavaTypeAdapterByName(@NonNull String name) { + return typeByName.get(name); + } + + /** + * Lookup a specific {@link IDataTypeAdapter} instance by its class. + * + * @param clazz + * the adapter class to get the instance for + * @param + * the type of the requested adapter + * @return the instance or {@code null} if the instance is unknown to the type + * system + */ + @SuppressWarnings("unchecked") + @Nullable + public > TYPE getJavaTypeAdapterByClass(@NonNull Class clazz) { + return (TYPE) typeByClass.get(clazz); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/IDataTypeAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/IDataTypeAdapter.java index 826c26f48..18c2f14b8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/IDataTypeAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/IDataTypeAdapter.java @@ -1,388 +1,388 @@ -/* - * 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.datatype; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.codehaus.stax2.XMLEventReader2; -import org.codehaus.stax2.XMLStreamWriter2; -import org.codehaus.stax2.evt.XMLEventFactory2; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.List; -import java.util.function.Supplier; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventWriter; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public interface IDataTypeAdapter { - - /** - * Get the metaschema type names associated with this adapter. This name must be - * unique with respect to all other metaschema types. - *

- * At least one name must be provided, with the first name being the most - * preferred name. - * - * @return the name - */ - @NonNull - List getNames(); - - /** - * The JSON primative type of the data type. - * - * @return the JSON data type - */ - JsonFormatTypes getJsonRawType(); - - /** - * Get the most preferred name for this data type. - * - * @return the name - */ - @NonNull - default String getPreferredName() { - return ObjectUtils.notNull(getNames().iterator().next()); - } - - /** - * Get the Java class supported by this adapter. - * - * @return the Java class - */ - @NonNull - Class getJavaClass(); - - /** - * Casts the provided value to the type associated with this adapter. - * - * @param value - * a value of the provided type - * @return the typed value - */ - @NonNull - TYPE toValue(@NonNull Object value); - - /** - * Gets the value as a string suitable for writing as text. This is intended for - * data types that have a simple string-based structure in XML and JSON, such as - * for XML attributes or JSON keys. An adapter for a complex data structures - * that consist of XML elements will throw an - * {@link UnsupportedOperationException} when this is called. - * - * @param value - * the data to formatted as a string - * @return a string - * @throws UnsupportedOperationException - * if the data type cannot be represented as a string - */ - @NonNull - String asString(@NonNull Object value); - - /** - * Create a copy of the provided value. - * - * @param obj - * the value to copy - * @return the copy - */ - @NonNull - TYPE copy(@NonNull Object obj); - - /** - * Determines if the data type is an atomic, scalar value. Complex structures - * such as Markup are not considered atomic. - * - * @return {@code true} if the data type is an atomic scalar value, or - * {@code false} otherwise - */ - default boolean isAtomic() { - return true; - } - - /** - * Get the java type of the associated item. - * - * @return the java associated item type - */ - @NonNull - Class getItemClass(); - - /** - * Construct a new item of this type using the provided value. - * - * @param value - * the item's value - * @return a new item - */ - // TODO: markup types are not atomic values. Figure out a better base type - // (i.e., IValuedItem) - @NonNull - IAnyAtomicItem newItem(@NonNull Object value); - - /** - * Cast the provided item to an item of this type, if possible. - * - * @param item - * the atomic item to cast - * @return an atomic item of this type - * @throws InvalidValueForCastFunctionException - * if the provided item type cannot be cast to this item type - */ - @NonNull - IAnyAtomicItem cast(IAnyAtomicItem item); - - /** - * Indicates if the adapter will parse the {@link XMLEvent#START_ELEMENT} before - * parsing the value data. - * - * @return {@code true} if the adapter requires the start element for parsing, - * or {@code false} otherwise - */ - // TODO; implement or remove this - boolean isParsingStartElement(); - - /** - * Determines if adapter can parse the next element. The next element's - * {@link QName} is provided for this purpose. - *

- * This will be called when the parser encounter's an element it does not - * recognize. This gives the adapter a chance to request parsing of the data. - * - * @param nextElementQName - * the next element's namespace-qualified name - * @return {@code true} if the adapter will parse the element, or {@code false} - * otherwise - */ - boolean canHandleQName(@NonNull QName nextElementQName); - - /** - * Parses a provided string. Used to parse XML attributes, simple XML character - * data, and JSON/YAML property values. - * - * @param value - * the string value to parse - * @return the parsed data as the adapter's type - * @throws IllegalArgumentException - * if the data is not valid to the data type - */ - @NonNull - TYPE parse(@NonNull String value); - - /** - * This method is expected to parse content starting at the next event. Parsing - * will continue until the next event represents content that is not handled by - * this adapter. This means the event stream should be positioned after any - * {@link XMLEvent#END_ELEMENT} that corresponds to an - * {@link XMLEvent#START_ELEMENT} parsed by this adapter. - *

- * If {@link #isParsingStartElement()} returns {@code true}, then the first - * event to parse will be the {@link XMLEvent#START_ELEMENT} for the element - * that contains the value data, then the value data. If this is the case, this - * method must also parse the corresponding {@link XMLEvent#END_ELEMENT}. - * Otherwise, the first event to parse will be the value data. - *

- * The value data is expected to be parsed completely, leaving the event stream - * on a peeked event corresponding to content that is not handled by this - * method. - * - * @param eventReader - * the XML parser used to read the parsed value - * @return the parsed value - * @throws IOException - * if a parsing error occurs - */ - @NonNull - TYPE parse(@NonNull XMLEventReader2 eventReader) throws IOException; - - /** - * Parses a JSON property value. - * - * @param parser - * the JSON parser used to read the parsed value - * @return the parsed value - * @throws IOException - * if a parsing error occurs - */ - @NonNull - TYPE parse(@NonNull JsonParser parser) throws IOException; - - /** - * Parses a provided string using {@link #parse(String)}. - *

- * This method may pre-parse the data and then return copies, since the data can - * only be parsed once, but the supplier might be called multiple times. - * - * @param value - * the string value to parse - * @return a supplier that will provide new instances of the parsed data - * @throws IOException - * if an error occurs while parsing - * @see #parse(String) - */ - @NonNull - default Supplier parseAndSupply(@NonNull String value) throws IOException { - TYPE retval = parse(value); - return () -> copy(retval); - } - - /** - * Parses a provided string using - * {@link IDataTypeAdapter#parse(XMLEventReader2)}. - *

- * This method may pre-parse the data and then return copies, since the data can - * only be parsed once, but the supplier might be called multiple times. - * - * @param eventReader - * the XML parser used to read the parsed value - * @return a supplier that will provide new instances of the parsed data - * @throws IOException - * if an error occurs while parsing - * @see #parse(String) - * @see #parse(XMLEventReader2) - */ - @NonNull - default Supplier parseAndSupply(@NonNull XMLEventReader2 eventReader) throws IOException { - TYPE retval = parse(eventReader); - return () -> copy(retval); - } - - /** - * Parses a provided string using {@link #parse(JsonParser)}. - *

- * This method may pre-parse the data and then return copies, since the data can - * only be parsed once, but the supplier might be called multiple times. - * - * @param parser - * the JSON parser used to read the parsed value - * @return a supplier that will provide new instances of the parsed data - * @throws IOException - * if an error occurs while parsing - * @see #parse(String) - * @see #parse(JsonParser) - */ - @NonNull - default Supplier parseAndSupply(@NonNull JsonParser parser) throws IOException { - TYPE retval = parse(parser); - return () -> copy(retval); - } - - /** - * Writes the provided Java class instance data as XML. The parent element - * information is provided as a {@link StartElement} event, which allows - * namespace information to be obtained from the parent element using the - * {@link StartElement#getName()} and {@link StartElement#getNamespaceContext()} - * methods, which can be used when writing the provided instance value. - * - * @param instance - * the {@link Field} instance value to write - * @param parent - * the {@link StartElement} XML event that is the parent of the data to - * write - * @param eventFactory - * the XML event factory used to generate XML writing events - * @param eventWriter - * the XML writer used to output XML as events - * @throws XMLStreamException - * if an unexpected error occurred while processing the XML output - * @throws IOException - * if an unexpected error occurred while writing to the output stream - */ - void writeXmlValue(@NonNull Object instance, @NonNull StartElement parent, @NonNull XMLEventFactory2 eventFactory, - @NonNull XMLEventWriter eventWriter) - throws IOException, XMLStreamException; - - /** - * Writes the provided Java class instance data as XML. The parent element - * information is provided as an XML {@link QName}, which allows namespace - * information to be obtained from the parent element. Additional namespace - * information can be gathered using the - * {@link XMLStreamWriter2#getNamespaceContext()} method, which can be used when - * writing the provided instance value. - * - * @param instance - * the {@link Field} instance value to write - * @param parentName - * the qualified name of the XML data's parent element - * @param writer - * the XML writer used to output the XML data - * @throws XMLStreamException - * if an unexpected error occurred while processing the XML output - */ - void writeXmlValue(@NonNull Object instance, @NonNull QName parentName, @NonNull XMLStreamWriter2 writer) - throws XMLStreamException; - - /** - * Writes the provided Java class instance as a JSON/YAML field value. - * - * @param instance - * the {@link Field} instance value to write - * @param writer - * the JSON/YAML writer used to output the JSON/YAML data - * @throws IOException - * if an unexpected error occurred while writing the JSON/YAML output - */ - void writeJsonValue(@NonNull Object instance, @NonNull JsonGenerator writer) throws IOException; - - /** - * Gets the default value to use as the JSON/YAML field name for a Metaschema - * field value if no JSON value key flag or name is configured. - * - * @return the default field name to use - */ - @NonNull - String getDefaultJsonValueKey(); - - /** - * Determines if the data type's value is allowed to be unwrapped in XML when - * the value is a field value. - * - * @return {@code true} if allowed, or {@code false} otherwise. - */ - boolean isUnrappedValueAllowedInXml(); - - /** - * Determines if the datatype uses mixed text and element content in XML. - * - * @return {@code true} if the datatype uses mixed text and element content in - * XML, or {@code false} otherwise - */ - boolean isXmlMixed(); -} +/* + * 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.datatype; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.codehaus.stax2.XMLEventReader2; +import org.codehaus.stax2.XMLStreamWriter2; +import org.codehaus.stax2.evt.XMLEventFactory2; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.List; +import java.util.function.Supplier; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public interface IDataTypeAdapter { + + /** + * Get the metaschema type names associated with this adapter. This name must be + * unique with respect to all other metaschema types. + *

+ * At least one name must be provided, with the first name being the most + * preferred name. + * + * @return the name + */ + @NonNull + List getNames(); + + /** + * Get the most preferred name for this data type. + * + * @return the name + */ + @NonNull + default QName getPreferredName() { + return ObjectUtils.notNull(getNames().iterator().next()); + } + + /** + * The JSON primative type of the data type. + * + * @return the JSON data type + */ + JsonFormatTypes getJsonRawType(); + + /** + * Get the Java class supported by this adapter. + * + * @return the Java class + */ + @NonNull + Class getJavaClass(); + + /** + * Casts the provided value to the type associated with this adapter. + * + * @param value + * a value of the provided type + * @return the typed value + */ + @NonNull + TYPE toValue(@NonNull Object value); + + /** + * Gets the value as a string suitable for writing as text. This is intended for + * data types that have a simple string-based structure in XML and JSON, such as + * for XML attributes or JSON keys. An adapter for a complex data structures + * that consist of XML elements will throw an + * {@link UnsupportedOperationException} when this is called. + * + * @param value + * the data to formatted as a string + * @return a string + * @throws UnsupportedOperationException + * if the data type cannot be represented as a string + */ + @NonNull + String asString(@NonNull Object value); + + /** + * Create a copy of the provided value. + * + * @param obj + * the value to copy + * @return the copy + */ + @NonNull + TYPE copy(@NonNull Object obj); + + /** + * Determines if the data type is an atomic, scalar value. Complex structures + * such as Markup are not considered atomic. + * + * @return {@code true} if the data type is an atomic scalar value, or + * {@code false} otherwise + */ + default boolean isAtomic() { + return true; + } + + /** + * Get the java type of the associated item. + * + * @return the java associated item type + */ + @NonNull + Class getItemClass(); + + /** + * Construct a new item of this type using the provided value. + * + * @param value + * the item's value + * @return a new item + */ + // TODO: markup types are not atomic values. Figure out a better base type + // (i.e., IValuedItem) + @NonNull + IAnyAtomicItem newItem(@NonNull Object value); + + /** + * Cast the provided item to an item of this type, if possible. + * + * @param item + * the atomic item to cast + * @return an atomic item of this type + * @throws InvalidValueForCastFunctionException + * if the provided item type cannot be cast to this item type + */ + @NonNull + IAnyAtomicItem cast(IAnyAtomicItem item); + + /** + * Indicates if the adapter will parse the {@link XMLEvent#START_ELEMENT} before + * parsing the value data. + * + * @return {@code true} if the adapter requires the start element for parsing, + * or {@code false} otherwise + */ + // TODO; implement or remove this + boolean isParsingStartElement(); + + /** + * Determines if adapter can parse the next element. The next element's + * {@link QName} is provided for this purpose. + *

+ * This will be called when the parser encounter's an element it does not + * recognize. This gives the adapter a chance to request parsing of the data. + * + * @param nextElementQName + * the next element's namespace-qualified name + * @return {@code true} if the adapter will parse the element, or {@code false} + * otherwise + */ + boolean canHandleQName(@NonNull QName nextElementQName); + + /** + * Parses a provided string. Used to parse XML attributes, simple XML character + * data, and JSON/YAML property values. + * + * @param value + * the string value to parse + * @return the parsed data as the adapter's type + * @throws IllegalArgumentException + * if the data is not valid to the data type + */ + @NonNull + TYPE parse(@NonNull String value); + + /** + * This method is expected to parse content starting at the next event. Parsing + * will continue until the next event represents content that is not handled by + * this adapter. This means the event stream should be positioned after any + * {@link XMLEvent#END_ELEMENT} that corresponds to an + * {@link XMLEvent#START_ELEMENT} parsed by this adapter. + *

+ * If {@link #isParsingStartElement()} returns {@code true}, then the first + * event to parse will be the {@link XMLEvent#START_ELEMENT} for the element + * that contains the value data, then the value data. If this is the case, this + * method must also parse the corresponding {@link XMLEvent#END_ELEMENT}. + * Otherwise, the first event to parse will be the value data. + *

+ * The value data is expected to be parsed completely, leaving the event stream + * on a peeked event corresponding to content that is not handled by this + * method. + * + * @param eventReader + * the XML parser used to read the parsed value + * @return the parsed value + * @throws IOException + * if a parsing error occurs + */ + @NonNull + TYPE parse(@NonNull XMLEventReader2 eventReader) throws IOException; + + /** + * Parses a JSON property value. + * + * @param parser + * the JSON parser used to read the parsed value + * @return the parsed value + * @throws IOException + * if a parsing error occurs + */ + @NonNull + TYPE parse(@NonNull JsonParser parser) throws IOException; + + /** + * Parses a provided string using {@link #parse(String)}. + *

+ * This method may pre-parse the data and then return copies, since the data can + * only be parsed once, but the supplier might be called multiple times. + * + * @param value + * the string value to parse + * @return a supplier that will provide new instances of the parsed data + * @throws IOException + * if an error occurs while parsing + * @see #parse(String) + */ + @NonNull + default Supplier parseAndSupply(@NonNull String value) throws IOException { + TYPE retval = parse(value); + return () -> copy(retval); + } + + /** + * Parses a provided string using + * {@link IDataTypeAdapter#parse(XMLEventReader2)}. + *

+ * This method may pre-parse the data and then return copies, since the data can + * only be parsed once, but the supplier might be called multiple times. + * + * @param eventReader + * the XML parser used to read the parsed value + * @return a supplier that will provide new instances of the parsed data + * @throws IOException + * if an error occurs while parsing + * @see #parse(String) + * @see #parse(XMLEventReader2) + */ + @NonNull + default Supplier parseAndSupply(@NonNull XMLEventReader2 eventReader) throws IOException { + TYPE retval = parse(eventReader); + return () -> copy(retval); + } + + /** + * Parses a provided string using {@link #parse(JsonParser)}. + *

+ * This method may pre-parse the data and then return copies, since the data can + * only be parsed once, but the supplier might be called multiple times. + * + * @param parser + * the JSON parser used to read the parsed value + * @return a supplier that will provide new instances of the parsed data + * @throws IOException + * if an error occurs while parsing + * @see #parse(String) + * @see #parse(JsonParser) + */ + @NonNull + default Supplier parseAndSupply(@NonNull JsonParser parser) throws IOException { + TYPE retval = parse(parser); + return () -> copy(retval); + } + + /** + * Writes the provided Java class instance data as XML. The parent element + * information is provided as a {@link StartElement} event, which allows + * namespace information to be obtained from the parent element using the + * {@link StartElement#getName()} and {@link StartElement#getNamespaceContext()} + * methods, which can be used when writing the provided instance value. + * + * @param instance + * the {@link Field} instance value to write + * @param parent + * the {@link StartElement} XML event that is the parent of the data to + * write + * @param eventFactory + * the XML event factory used to generate XML writing events + * @param eventWriter + * the XML writer used to output XML as events + * @throws XMLStreamException + * if an unexpected error occurred while processing the XML output + * @throws IOException + * if an unexpected error occurred while writing to the output stream + */ + void writeXmlValue(@NonNull Object instance, @NonNull StartElement parent, @NonNull XMLEventFactory2 eventFactory, + @NonNull XMLEventWriter eventWriter) + throws IOException, XMLStreamException; + + /** + * Writes the provided Java class instance data as XML. The parent element + * information is provided as an XML {@link QName}, which allows namespace + * information to be obtained from the parent element. Additional namespace + * information can be gathered using the + * {@link XMLStreamWriter2#getNamespaceContext()} method, which can be used when + * writing the provided instance value. + * + * @param instance + * the {@link Field} instance value to write + * @param parentName + * the qualified name of the XML data's parent element + * @param writer + * the XML writer used to output the XML data + * @throws XMLStreamException + * if an unexpected error occurred while processing the XML output + */ + void writeXmlValue(@NonNull Object instance, @NonNull QName parentName, @NonNull XMLStreamWriter2 writer) + throws XMLStreamException; + + /** + * Writes the provided Java class instance as a JSON/YAML field value. + * + * @param instance + * the {@link Field} instance value to write + * @param writer + * the JSON/YAML writer used to output the JSON/YAML data + * @throws IOException + * if an unexpected error occurred while writing the JSON/YAML output + */ + void writeJsonValue(@NonNull Object instance, @NonNull JsonGenerator writer) throws IOException; + + /** + * Gets the default value to use as the JSON/YAML field name for a Metaschema + * field value if no JSON value key flag or name is configured. + * + * @return the default field name to use + */ + @NonNull + String getDefaultJsonValueKey(); + + /** + * Determines if the data type's value is allowed to be unwrapped in XML when + * the value is a field value. + * + * @return {@code true} if allowed, or {@code false} otherwise. + */ + boolean isUnrappedValueAllowedInXml(); + + /** + * Determines if the datatype uses mixed text and element content in XML. + * + * @return {@code true} if the datatype uses mixed text and element content in + * XML, or {@code false} otherwise + */ + boolean isXmlMixed(); +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/Base64Adapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/Base64Adapter.java index 4e40ba80d..abd644013 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/Base64Adapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/Base64Adapter.java @@ -1,101 +1,104 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.nio.ByteBuffer; -import java.util.Base64; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class Base64Adapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "base64", - // for backwards compatibility with original type name - "base64Binary")); - - Base64Adapter() { - super(ByteBuffer.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public ByteBuffer parse(String value) { - Base64.Decoder decoder = Base64.getDecoder(); - byte[] result = decoder.decode(value); - return ByteBuffer.wrap(result); - } - - @Override - public ByteBuffer copy(Object obj) { - ByteBuffer buffer = (ByteBuffer) obj; - final ByteBuffer clone - = buffer.isDirect() ? ByteBuffer.allocateDirect(buffer.capacity()) : ByteBuffer.allocate(buffer.capacity()); - final ByteBuffer readOnlyCopy = buffer.asReadOnlyBuffer(); - readOnlyCopy.flip(); - clone.put(readOnlyCopy); - return clone; - } - - @SuppressWarnings("null") - @Override - public String asString(Object value) { - Base64.Encoder encoder = Base64.getEncoder(); - return encoder.encodeToString(((ByteBuffer) value).array()); - } - - @Override - public Class getItemClass() { - return IBase64BinaryItem.class; - } - - @Override - public IBase64BinaryItem newItem(Object value) { - ByteBuffer item = toValue(value); - return IBase64BinaryItem.valueOf(item); - } - -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.nio.ByteBuffer; +import java.util.Base64; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class Base64Adapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "base64"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "base64Binary"))); + + Base64Adapter() { + super(ByteBuffer.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public ByteBuffer parse(String value) { + Base64.Decoder decoder = Base64.getDecoder(); + byte[] result = decoder.decode(value); + return ByteBuffer.wrap(result); + } + + @Override + public ByteBuffer copy(Object obj) { + ByteBuffer buffer = (ByteBuffer) obj; + final ByteBuffer clone + = buffer.isDirect() ? ByteBuffer.allocateDirect(buffer.capacity()) : ByteBuffer.allocate(buffer.capacity()); + final ByteBuffer readOnlyCopy = buffer.asReadOnlyBuffer(); + readOnlyCopy.flip(); + clone.put(readOnlyCopy); + return clone; + } + + @SuppressWarnings("null") + @Override + public String asString(Object value) { + Base64.Encoder encoder = Base64.getEncoder(); + return encoder.encodeToString(((ByteBuffer) value).array()); + } + + @Override + public Class getItemClass() { + return IBase64BinaryItem.class; + } + + @Override + public IBase64BinaryItem newItem(Object value) { + ByteBuffer item = toValue(value); + return IBase64BinaryItem.valueOf(item); + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/BooleanAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/BooleanAdapter.java index 133f8f0dc..9fa446493 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/BooleanAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/BooleanAdapter.java @@ -1,156 +1,159 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.io.IOException; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class BooleanAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("boolean")); - - BooleanAdapter() { - super(Boolean.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.BOOLEAN; - } - - @SuppressWarnings("null") - @Override - public Boolean parse(String value) { - return Boolean.valueOf(value); - } - - @Override - public Boolean parse(JsonParser parser) throws IOException { - Boolean value = parser.getBooleanValue(); - // skip over value - parser.nextToken(); - return value; - } - - @Override - public void writeJsonValue(Object value, JsonGenerator generator) - throws IOException { - try { - generator.writeBoolean((Boolean) value); - } catch (ClassCastException ex) { - throw new IOException(ex); - } - } - - @Override - public Boolean copy(Object obj) { - // the value is immutable - return (Boolean) obj; - } - - @Override - public Class getItemClass() { - return IBooleanItem.class; - } - - @Override - public IBooleanItem newItem(Object value) { - Boolean item = toValue(value); - return IBooleanItem.valueOf(item); - } - - @Override - protected IBooleanItem castInternal(@NonNull IAnyAtomicItem item) { - IBooleanItem retval; - if (item instanceof INumericItem) { - retval = castToBoolean((INumericItem) item); - } else if (item instanceof IStringItem) { - retval = castToBoolean((IStringItem) item); - } else { - retval = castToBoolean(item.asStringItem()); - } - return retval; - } - - /** - * Cast the provided numeric value to a boolean. Any non-zero value will be - * {@code true}, or {@code false} otherwise. - * - * @param item - * the item to cast - * @return {@code true} if the item value is non-zero, or {@code false} - * otherwise - */ - @NonNull - protected IBooleanItem castToBoolean(@NonNull INumericItem item) { - return IBooleanItem.valueOf(item.toEffectiveBoolean()); - } - - /** - * If the string is a numeric value, treat it as so. Otherwise parse the value - * as a boolean string. - * - * @param item - * the item to cast - * @return the effective boolean value of the string - * @throws InvalidValueForCastFunctionException - * if the provided item cannot be cast to a boolean value by any means - */ - @NonNull - protected IBooleanItem castToBoolean(@NonNull IStringItem item) { - IBooleanItem retval; - try { - INumericItem numeric = INumericItem.cast(item); - retval = castToBoolean(numeric); - } catch (InvalidValueForCastFunctionException ex) { - retval = super.castInternal(item); - } - return retval; - } - -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.io.IOException; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class BooleanAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "boolean"))); + + BooleanAdapter() { + super(Boolean.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.BOOLEAN; + } + + @SuppressWarnings("null") + @Override + public Boolean parse(String value) { + return Boolean.valueOf(value); + } + + @Override + public Boolean parse(JsonParser parser) throws IOException { + Boolean value = parser.getBooleanValue(); + // skip over value + parser.nextToken(); + return value; + } + + @Override + public void writeJsonValue(Object value, JsonGenerator generator) + throws IOException { + try { + generator.writeBoolean((Boolean) value); + } catch (ClassCastException ex) { + throw new IOException(ex); + } + } + + @Override + public Boolean copy(Object obj) { + // the value is immutable + return (Boolean) obj; + } + + @Override + public Class getItemClass() { + return IBooleanItem.class; + } + + @Override + public IBooleanItem newItem(Object value) { + Boolean item = toValue(value); + return IBooleanItem.valueOf(item); + } + + @Override + protected IBooleanItem castInternal(@NonNull IAnyAtomicItem item) { + IBooleanItem retval; + if (item instanceof INumericItem) { + retval = castToBoolean((INumericItem) item); + } else if (item instanceof IStringItem) { + retval = castToBoolean((IStringItem) item); + } else { + retval = castToBoolean(item.asStringItem()); + } + return retval; + } + + /** + * Cast the provided numeric value to a boolean. Any non-zero value will be + * {@code true}, or {@code false} otherwise. + * + * @param item + * the item to cast + * @return {@code true} if the item value is non-zero, or {@code false} + * otherwise + */ + @NonNull + protected IBooleanItem castToBoolean(@NonNull INumericItem item) { + return IBooleanItem.valueOf(item.toEffectiveBoolean()); + } + + /** + * If the string is a numeric value, treat it as so. Otherwise parse the value + * as a boolean string. + * + * @param item + * the item to cast + * @return the effective boolean value of the string + * @throws InvalidValueForCastFunctionException + * if the provided item cannot be cast to a boolean value by any means + */ + @NonNull + protected IBooleanItem castToBoolean(@NonNull IStringItem item) { + IBooleanItem retval; + try { + INumericItem numeric = INumericItem.cast(item); + retval = castToBoolean(numeric); + } catch (InvalidValueForCastFunctionException ex) { + retval = super.castInternal(item); + } + return retval; + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapter.java index 349431e9f..c3beb3d3e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapter.java @@ -1,147 +1,150 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractCustomJavaDataTypeAdapter; -import gov.nist.secauto.metaschema.core.datatype.object.Date; -import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; -import java.time.temporal.TemporalAccessor; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DateAdapter - extends AbstractCustomJavaDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("date")); - private static final Pattern DATE_TIMEZONE = Pattern.compile("^(" - + "^(?:(?:2000|2400|2800|(?:19|2[0-9](?:0[48]|[2468][048]|[13579][26])))-02-29)" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-02-(?:0[1-9]|1[0-9]|2[0-8]))" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[13578]|10|12)-(?:0[1-9]|[12][0-9]|3[01]))" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30))" - + ")" - + "(Z|[+-][0-9]{2}:[0-9]{2})?$"); - - DateAdapter() { - super(Date.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @Override - public Date parse(String value) { - Matcher matcher = DATE_TIMEZONE.matcher(value); - if (!matcher.matches()) { - throw new IllegalArgumentException("Invalid date: " + value); - } - - String parseValue - = String.format("%sT00:00:00%s", matcher.group(1), matcher.group(2) == null ? "" : matcher.group(2)); - try { - TemporalAccessor accessor = DateFormats.DATE_TIME_WITH_TZ.parse(parseValue); - return new Date(ObjectUtils.notNull(ZonedDateTime.from(accessor)), true); // NOPMD - readability - } catch (DateTimeParseException ex) { - try { - TemporalAccessor accessor = DateFormats.DATE_TIME_WITHOUT_TZ.parse(parseValue); - LocalDate date = LocalDate.from(accessor); - return new Date(ObjectUtils.notNull(ZonedDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC)), false); - } catch (DateTimeParseException ex2) { - IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); - newEx.addSuppressed(ex); - throw newEx; // NOPMD - false positive - } - } - } - - @Override - public String asString(Object obj) { - Date value = (Date) obj; - String retval; - if (value.hasTimeZone()) { - @SuppressWarnings("null") - @NonNull String formatted = DateFormats.DATE_WITH_TZ.format(value.getValue()); - retval = formatted; - } else { - @SuppressWarnings("null") - @NonNull String formatted = DateFormats.DATE_WITHOUT_TZ.format(value.getValue()); - retval = formatted; - } - return retval; - } - - @Override - public Class getItemClass() { - return IDateItem.class; - } - - @Override - public IDateItem newItem(Object value) { - Date item = toValue(value); - return IDateItem.valueOf(item); - } - - @Override - protected @NonNull IDateItem castInternal(@NonNull IAnyAtomicItem item) { - IDateItem retval; - if (item instanceof IDateTimeItem) { - ZonedDateTime value = ((IDateTimeItem) item).asZonedDateTime(); - retval = IDateItem.valueOf(value); - } else if (item instanceof IStringItem || item instanceof IUntypedAtomicItem) { - retval = super.castInternal(item); - } else { - throw new InvalidValueForCastFunctionException( - String.format("unsupported item type '%s'", item.getClass().getName())); - } - return retval; - } - -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractCustomJavaDataTypeAdapter; +import gov.nist.secauto.metaschema.core.datatype.object.Date; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DateAdapter + extends AbstractCustomJavaDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "date"))); + private static final Pattern DATE_TIMEZONE = Pattern.compile("^(" + + "^(?:(?:2000|2400|2800|(?:19|2[0-9](?:0[48]|[2468][048]|[13579][26])))-02-29)" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-02-(?:0[1-9]|1[0-9]|2[0-8]))" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[13578]|10|12)-(?:0[1-9]|[12][0-9]|3[01]))" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30))" + + ")" + + "(Z|[+-][0-9]{2}:[0-9]{2})?$"); + + DateAdapter() { + super(Date.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @Override + public Date parse(String value) { + Matcher matcher = DATE_TIMEZONE.matcher(value); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid date: " + value); + } + + String parseValue + = String.format("%sT00:00:00%s", matcher.group(1), matcher.group(2) == null ? "" : matcher.group(2)); + try { + TemporalAccessor accessor = DateFormats.DATE_TIME_WITH_TZ.parse(parseValue); + return new Date(ObjectUtils.notNull(ZonedDateTime.from(accessor)), true); // NOPMD - readability + } catch (DateTimeParseException ex) { + try { + TemporalAccessor accessor = DateFormats.DATE_TIME_WITHOUT_TZ.parse(parseValue); + LocalDate date = LocalDate.from(accessor); + return new Date(ObjectUtils.notNull(ZonedDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC)), false); + } catch (DateTimeParseException ex2) { + IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); + newEx.addSuppressed(ex); + throw newEx; // NOPMD - false positive + } + } + } + + @Override + public String asString(Object obj) { + Date value = (Date) obj; + String retval; + if (value.hasTimeZone()) { + @SuppressWarnings("null") + @NonNull String formatted = DateFormats.DATE_WITH_TZ.format(value.getValue()); + retval = formatted; + } else { + @SuppressWarnings("null") + @NonNull String formatted = DateFormats.DATE_WITHOUT_TZ.format(value.getValue()); + retval = formatted; + } + return retval; + } + + @Override + public Class getItemClass() { + return IDateItem.class; + } + + @Override + public IDateItem newItem(Object value) { + Date item = toValue(value); + return IDateItem.valueOf(item); + } + + @Override + protected @NonNull IDateItem castInternal(@NonNull IAnyAtomicItem item) { + IDateItem retval; + if (item instanceof IDateTimeItem) { + ZonedDateTime value = ((IDateTimeItem) item).asZonedDateTime(); + retval = IDateItem.valueOf(value); + } else if (item instanceof IStringItem || item instanceof IUntypedAtomicItem) { + retval = super.castInternal(item); + } else { + throw new InvalidValueForCastFunctionException( + String.format("unsupported item type '%s'", item.getClass().getName())); + } + return retval; + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapter.java index 90ca1d9e9..0573b3b3f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapter.java @@ -1,131 +1,134 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractCustomJavaDataTypeAdapter; -import gov.nist.secauto.metaschema.core.datatype.object.DateTime; -import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DateTimeAdapter - extends AbstractCustomJavaDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "date-time", - // for backwards compatibility with original type name - "dateTime")); - - DateTimeAdapter() { - super(DateTime.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public DateTime parse(String value) { - try { - return new DateTime(ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value)), true); // NOPMD - readability - } catch (DateTimeParseException ex) { - try { - LocalDateTime dateTime = LocalDateTime.from(DateFormats.DATE_TIME_WITHOUT_TZ.parse(value)); - return new DateTime(ZonedDateTime.of(dateTime, ZoneOffset.UTC), false); - } catch (DateTimeParseException ex2) { - IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); - newEx.addSuppressed(ex); - throw newEx; // NOPMD - it's ok - } - } - } - - @Override - public String asString(Object obj) { - DateTime value = (DateTime) obj; - String retval; - if (value.hasTimeZone()) { - @SuppressWarnings("null") - @NonNull String formatted = DateFormats.DATE_TIME_WITH_TZ.format(value.getValue()); - retval = formatted; - } else { - @SuppressWarnings("null") - @NonNull String formatted = DateFormats.DATE_TIME_WITHOUT_TZ.format(value.getValue()); - retval = formatted; - } - return retval; - } - - @Override - public Class getItemClass() { - return IDateTimeItem.class; - } - - @Override - public IDateTimeItem newItem(Object value) { - DateTime item = toValue(value); - return IDateTimeItem.valueOf(item); - } - - @Override - protected IDateTimeItem castInternal(@NonNull IAnyAtomicItem item) { - // TODO: bring up to spec - IDateTimeItem retval; - if (item instanceof IDateItem) { - retval = IDateTimeItem.valueOf(((IDateItem) item).asZonedDateTime()); - } else if (item instanceof IStringItem || item instanceof IUntypedAtomicItem) { - retval = super.castInternal(item); - } else { - throw new InvalidValueForCastFunctionException( - String.format("unsupported item type '%s'", item.getClass().getName())); - } - return retval; - } - -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractCustomJavaDataTypeAdapter; +import gov.nist.secauto.metaschema.core.datatype.object.DateTime; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DateTimeAdapter + extends AbstractCustomJavaDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "date-time"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "dateTime"))); + + DateTimeAdapter() { + super(DateTime.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public DateTime parse(String value) { + try { + return new DateTime(ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value)), true); // NOPMD - readability + } catch (DateTimeParseException ex) { + try { + LocalDateTime dateTime = LocalDateTime.from(DateFormats.DATE_TIME_WITHOUT_TZ.parse(value)); + return new DateTime(ZonedDateTime.of(dateTime, ZoneOffset.UTC), false); + } catch (DateTimeParseException ex2) { + IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); + newEx.addSuppressed(ex); + throw newEx; // NOPMD - it's ok + } + } + } + + @Override + public String asString(Object obj) { + DateTime value = (DateTime) obj; + String retval; + if (value.hasTimeZone()) { + @SuppressWarnings("null") + @NonNull String formatted = DateFormats.DATE_TIME_WITH_TZ.format(value.getValue()); + retval = formatted; + } else { + @SuppressWarnings("null") + @NonNull String formatted = DateFormats.DATE_TIME_WITHOUT_TZ.format(value.getValue()); + retval = formatted; + } + return retval; + } + + @Override + public Class getItemClass() { + return IDateTimeItem.class; + } + + @Override + public IDateTimeItem newItem(Object value) { + DateTime item = toValue(value); + return IDateTimeItem.valueOf(item); + } + + @Override + protected IDateTimeItem castInternal(@NonNull IAnyAtomicItem item) { + // TODO: bring up to spec + IDateTimeItem retval; + if (item instanceof IDateItem) { + retval = IDateTimeItem.valueOf(((IDateItem) item).asZonedDateTime()); + } else if (item instanceof IStringItem || item instanceof IUntypedAtomicItem) { + retval = super.castInternal(item); + } else { + throw new InvalidValueForCastFunctionException( + String.format("unsupported item type '%s'", item.getClass().getName())); + } + return retval; + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeWithTZAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeWithTZAdapter.java index ea6c44537..1bfbf278e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeWithTZAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeWithTZAdapter.java @@ -1,96 +1,99 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DateTimeWithTZAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "date-time-with-timezone", - // for backwards compatibility with original type name - "dateTime-with-timezone")); - - DateTimeWithTZAdapter() { - super(ZonedDateTime.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public ZonedDateTime parse(String value) { - try { - return ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value)); - } catch (DateTimeParseException ex) { - throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); - } - } - - @SuppressWarnings("null") - @Override - public String asString(Object value) { - return DateFormats.DATE_TIME_WITH_TZ.format((ZonedDateTime) value); - } - - @SuppressWarnings("null") - @Override - public ZonedDateTime copy(Object obj) { - return ZonedDateTime.from((ZonedDateTime) obj); - } - - @Override - public Class getItemClass() { - return IDateTimeItem.class; - } - - @Override - public IDateTimeItem newItem(Object value) { - ZonedDateTime item = toValue(value); - return IDateTimeItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DateTimeWithTZAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "date-time-with-timezone"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "dateTime-with-timezone"))); + + DateTimeWithTZAdapter() { + super(ZonedDateTime.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public ZonedDateTime parse(String value) { + try { + return ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value)); + } catch (DateTimeParseException ex) { + throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); + } + } + + @SuppressWarnings("null") + @Override + public String asString(Object value) { + return DateFormats.DATE_TIME_WITH_TZ.format((ZonedDateTime) value); + } + + @SuppressWarnings("null") + @Override + public ZonedDateTime copy(Object obj) { + return ZonedDateTime.from((ZonedDateTime) obj); + } + + @Override + public Class getItemClass() { + return IDateTimeItem.class; + } + + @Override + public IDateTimeItem newItem(Object value) { + ZonedDateTime item = toValue(value); + return IDateTimeItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateWithTZAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateWithTZAdapter.java index f116e3c25..3d0040d7d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateWithTZAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateWithTZAdapter.java @@ -1,108 +1,111 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DateWithTZAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "date-with-timezone")); - private static final Pattern DATE_TIMEZONE = Pattern.compile("^(" - + "^(?:(?:2000|2400|2800|(?:19|2[0-9](?:0[48]|[2468][048]|[13579][26])))-02-29)" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-02-(?:0[1-9]|1[0-9]|2[0-8]))" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[13578]|10|12)-(?:0[1-9]|[12][0-9]|3[01]))" - + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30))" - + ")" - + "(Z|[+-][0-9]{2}:[0-9]{2})$"); - - DateWithTZAdapter() { - super(ZonedDateTime.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public ZonedDateTime parse(String value) { - Matcher matcher = DATE_TIMEZONE.matcher(value); - if (!matcher.matches()) { - throw new IllegalArgumentException("Invalid date: " + value); - } - String parseValue = String.format("%sT00:00:00%s", matcher.group(1), matcher.group(2)); - try { - return ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(parseValue)); - } catch (DateTimeParseException ex) { - throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); - } - } - - @SuppressWarnings("null") - @Override - public String asString(Object value) { - return DateFormats.DATE_WITH_TZ.format((ZonedDateTime) value); - } - - @SuppressWarnings("null") - @Override - public ZonedDateTime copy(Object obj) { - return ZonedDateTime.from((ZonedDateTime) obj); - } - - @Override - public Class getItemClass() { - return IDateItem.class; - } - - @Override - public IDateItem newItem(Object value) { - ZonedDateTime item = toValue(value); - return IDateItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DateWithTZAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "date-with-timezone"))); + private static final Pattern DATE_TIMEZONE = Pattern.compile("^(" + + "^(?:(?:2000|2400|2800|(?:19|2[0-9](?:0[48]|[2468][048]|[13579][26])))-02-29)" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-02-(?:0[1-9]|1[0-9]|2[0-8]))" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[13578]|10|12)-(?:0[1-9]|[12][0-9]|3[01]))" + + "|(?:(?:(?:19|2[0-9])[0-9]{2})-(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30))" + + ")" + + "(Z|[+-][0-9]{2}:[0-9]{2})$"); + + DateWithTZAdapter() { + super(ZonedDateTime.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public ZonedDateTime parse(String value) { + Matcher matcher = DATE_TIMEZONE.matcher(value); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid date: " + value); + } + String parseValue = String.format("%sT00:00:00%s", matcher.group(1), matcher.group(2)); + try { + return ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(parseValue)); + } catch (DateTimeParseException ex) { + throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); + } + } + + @SuppressWarnings("null") + @Override + public String asString(Object value) { + return DateFormats.DATE_WITH_TZ.format((ZonedDateTime) value); + } + + @SuppressWarnings("null") + @Override + public ZonedDateTime copy(Object obj) { + return ZonedDateTime.from((ZonedDateTime) obj); + } + + @Override + public Class getItemClass() { + return IDateItem.class; + } + + @Override + public IDateItem newItem(Object value) { + ZonedDateTime item = toValue(value); + return IDateItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DayTimeAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DayTimeAdapter.java index 9e2e2c795..b058f2eac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DayTimeAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DayTimeAdapter.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -36,20 +37,22 @@ import java.time.format.DateTimeParseException; import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class DayTimeAdapter extends AbstractDataTypeAdapter { @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("day-time-duration")); + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "day-time-duration"))); DayTimeAdapter() { super(Duration.class); } @Override - public List getNames() { + public List getNames() { return NAMES; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DecimalAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DecimalAdapter.java index 0754a7cfd..49fd4ee29 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DecimalAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/DecimalAdapter.java @@ -1,115 +1,118 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DecimalAdapter - extends AbstractDataTypeAdapter { - public static final MathContext MATH_CONTEXT = MathContext.DECIMAL64; - @NonNull - private static final BigDecimal DECIMAL_BOOLEAN_TRUE = new BigDecimal("1.0"); - @NonNull - private static final BigDecimal DECIMAL_BOOLEAN_FALSE = new BigDecimal("0.0"); - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("decimal")); - - DecimalAdapter() { - super(BigDecimal.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.NUMBER; - } - - @Override - public BigDecimal parse(String value) { - return new BigDecimal(value, MATH_CONTEXT); - } - - @Override - public void writeJsonValue(Object value, JsonGenerator generator) throws IOException { - try { - generator.writeNumber((BigDecimal) value); - } catch (ClassCastException ex) { - throw new IOException(ex); - } - } - - @Override - public BigDecimal copy(Object obj) { - // a BigDecimal is immutable - return (BigDecimal) obj; - } - - @Override - public Class getItemClass() { - return IDecimalItem.class; - } - - @Override - public IDecimalItem newItem(Object value) { - BigDecimal item = toValue(value); - return IDecimalItem.valueOf(item); - } - - @Override - protected IDecimalItem castInternal(@NonNull IAnyAtomicItem item) { - IDecimalItem retval; - if (item instanceof INumericItem) { - retval = newItem(((INumericItem) item).asDecimal()); - } else if (item instanceof IBooleanItem) { - boolean value = ((IBooleanItem) item).toBoolean(); - retval = newItem(value ? DECIMAL_BOOLEAN_TRUE : DECIMAL_BOOLEAN_FALSE); - } else { - retval = super.castInternal(item); - } - return retval; - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DecimalAdapter + extends AbstractDataTypeAdapter { + public static final MathContext MATH_CONTEXT = MathContext.DECIMAL64; + @NonNull + private static final BigDecimal DECIMAL_BOOLEAN_TRUE = new BigDecimal("1.0"); + @NonNull + private static final BigDecimal DECIMAL_BOOLEAN_FALSE = new BigDecimal("0.0"); + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "decimal"))); + + DecimalAdapter() { + super(BigDecimal.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.NUMBER; + } + + @Override + public BigDecimal parse(String value) { + return new BigDecimal(value, MATH_CONTEXT); + } + + @Override + public void writeJsonValue(Object value, JsonGenerator generator) throws IOException { + try { + generator.writeNumber((BigDecimal) value); + } catch (ClassCastException ex) { + throw new IOException(ex); + } + } + + @Override + public BigDecimal copy(Object obj) { + // a BigDecimal is immutable + return (BigDecimal) obj; + } + + @Override + public Class getItemClass() { + return IDecimalItem.class; + } + + @Override + public IDecimalItem newItem(Object value) { + BigDecimal item = toValue(value); + return IDecimalItem.valueOf(item); + } + + @Override + protected IDecimalItem castInternal(@NonNull IAnyAtomicItem item) { + IDecimalItem retval; + if (item instanceof INumericItem) { + retval = newItem(((INumericItem) item).asDecimal()); + } else if (item instanceof IBooleanItem) { + boolean value = ((IBooleanItem) item).toBoolean(); + retval = newItem(value ? DECIMAL_BOOLEAN_TRUE : DECIMAL_BOOLEAN_FALSE); + } else { + retval = super.castInternal(item); + } + return retval; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/EmailAddressAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/EmailAddressAdapter.java index 13b9b8657..9818890a2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/EmailAddressAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/EmailAddressAdapter.java @@ -1,64 +1,67 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IEmailAddressItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class EmailAddressAdapter - extends AbstractStringAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "email-address", - // for backwards compatibility with original type name - "email")); - - EmailAddressAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public @NonNull Class getItemClass() { - return IEmailAddressItem.class; - } - - @Override - public IEmailAddressItem newItem(Object value) { - String item = asString(value); - return IEmailAddressItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IEmailAddressItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class EmailAddressAdapter + extends AbstractStringAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "email-address"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "email"))); + + EmailAddressAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public @NonNull Class getItemClass() { + return IEmailAddressItem.class; + } + + @Override + public IEmailAddressItem newItem(Object value) { + String item = asString(value); + return IEmailAddressItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/HostnameAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/HostnameAdapter.java index 30304b8aa..6b6a8455c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/HostnameAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/HostnameAdapter.java @@ -1,61 +1,64 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IHostnameItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class HostnameAdapter - extends AbstractStringAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("hostname")); - - HostnameAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public @NonNull Class getItemClass() { - return IHostnameItem.class; - } - - @Override - public IHostnameItem newItem(Object value) { - String item = asString(value); - return IHostnameItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IHostnameItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class HostnameAdapter + extends AbstractStringAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "hostname"))); + + HostnameAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public @NonNull Class getItemClass() { + return IHostnameItem.class; + } + + @Override + public IHostnameItem newItem(Object value) { + String item = asString(value); + return IHostnameItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv4AddressAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv4AddressAdapter.java index 5c6aeb585..58ad33492 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv4AddressAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv4AddressAdapter.java @@ -1,97 +1,108 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv4AddressItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; -import inet.ipaddr.AddressStringException; -import inet.ipaddr.IPAddressString; -import inet.ipaddr.IPAddressStringParameters; -import inet.ipaddr.IncompatibleAddressException; -import inet.ipaddr.ipv4.IPv4Address; - -public class IPv4AddressAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("ip-v4-address")); - private static final IPAddressStringParameters IP_V_4; - - static { - IP_V_4 = new IPAddressStringParameters.Builder().allowIPv6(false).allowEmpty(false).allowSingleSegment(false) - .allowWildcardedSeparator(false).getIPv4AddressParametersBuilder().allowBinary(false).allowLeadingZeros(false) - .allowPrefixesBeyondAddressSize(false).getParentBuilder().toParams(); - } - - IPv4AddressAdapter() { - super(IPv4Address.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public IPv4Address parse(String value) { - try { - return (IPv4Address) new IPAddressString(value, IP_V_4).toAddress(); - } catch (AddressStringException | IncompatibleAddressException ex) { - throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); - } - } - - @Override - public IPv4Address copy(Object obj) { - // value is immutable - return (IPv4Address) obj; - } - - @Override - public Class getItemClass() { - return IIPv4AddressItem.class; - } - - @Override - public IIPv4AddressItem newItem(Object value) { - IPv4Address item = toValue(value); - return IIPv4AddressItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv4AddressItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; +import inet.ipaddr.AddressStringException; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.IPAddressStringParameters; +import inet.ipaddr.IncompatibleAddressException; +import inet.ipaddr.ipv4.IPv4Address; + +public class IPv4AddressAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "ip-v4-address"))); + private static final IPAddressStringParameters IP_V_4; + + static { + IP_V_4 = new IPAddressStringParameters.Builder() + .allowIPv6(false) + .allowEmpty(false) + .allowSingleSegment(false) + .allowWildcardedSeparator(false) + .getIPv4AddressParametersBuilder() + .allowBinary(false) + .allowLeadingZeros(false) + .allowPrefixesBeyondAddressSize(false) + .getParentBuilder() + .toParams(); + } + + IPv4AddressAdapter() { + super(IPv4Address.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public IPv4Address parse(String value) { + try { + return (IPv4Address) new IPAddressString(value, IP_V_4).toAddress(); + } catch (AddressStringException | IncompatibleAddressException ex) { + throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); + } + } + + @Override + public IPv4Address copy(Object obj) { + // value is immutable + return (IPv4Address) obj; + } + + @Override + public Class getItemClass() { + return IIPv4AddressItem.class; + } + + @Override + public IIPv4AddressItem newItem(Object value) { + IPv4Address item = toValue(value); + return IIPv4AddressItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv6AddressAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv6AddressAdapter.java index 05ba1bbca..a09300569 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv6AddressAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IPv6AddressAdapter.java @@ -1,97 +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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv6AddressItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; -import inet.ipaddr.AddressStringException; -import inet.ipaddr.IPAddressString; -import inet.ipaddr.IPAddressStringParameters; -import inet.ipaddr.IncompatibleAddressException; -import inet.ipaddr.ipv6.IPv6Address; - -public class IPv6AddressAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("ip-v6-address")); - private static final IPAddressStringParameters IP_V_6; - - static { - IP_V_6 = new IPAddressStringParameters.Builder().allowIPv4(false).allowEmpty(false).allowSingleSegment(false) - .allowWildcardedSeparator(false).getIPv6AddressParametersBuilder().allowBinary(false) - .allowPrefixesBeyondAddressSize(false).getParentBuilder().toParams(); - } - - IPv6AddressAdapter() { - super(IPv6Address.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public IPv6Address parse(String value) { - try { - return (IPv6Address) new IPAddressString(value, IP_V_6).toAddress(); - } catch (AddressStringException | IncompatibleAddressException ex) { - throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); - } - } - - @Override - public IPv6Address copy(Object obj) { - // value is immutable - return (IPv6Address) obj; - } - - @Override - public Class getItemClass() { - return IIPv6AddressItem.class; - } - - @Override - public IIPv6AddressItem newItem(Object value) { - IPv6Address item = toValue(value); - return IIPv6AddressItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv6AddressItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; +import inet.ipaddr.AddressStringException; +import inet.ipaddr.IPAddressString; +import inet.ipaddr.IPAddressStringParameters; +import inet.ipaddr.IncompatibleAddressException; +import inet.ipaddr.ipv6.IPv6Address; + +public class IPv6AddressAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "ip-v6-address"))); + private static final IPAddressStringParameters IP_V_6; + + static { + IP_V_6 = new IPAddressStringParameters.Builder().allowIPv4(false).allowEmpty(false).allowSingleSegment(false) + .allowWildcardedSeparator(false).getIPv6AddressParametersBuilder().allowBinary(false) + .allowPrefixesBeyondAddressSize(false).getParentBuilder().toParams(); + } + + IPv6AddressAdapter() { + super(IPv6Address.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public IPv6Address parse(String value) { + try { + return (IPv6Address) new IPAddressString(value, IP_V_6).toAddress(); + } catch (AddressStringException | IncompatibleAddressException ex) { + throw new IllegalArgumentException(ex.getLocalizedMessage(), ex); + } + } + + @Override + public IPv6Address copy(Object obj) { + // value is immutable + return (IPv6Address) obj; + } + + @Override + public Class getItemClass() { + return IIPv6AddressItem.class; + } + + @Override + public IIPv6AddressItem newItem(Object value) { + IPv6Address item = toValue(value); + return IIPv6AddressItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IntegerAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IntegerAdapter.java index 2e0684092..02973460d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IntegerAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/IntegerAdapter.java @@ -1,79 +1,82 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.math.BigInteger; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class IntegerAdapter - extends AbstractIntegerAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("integer")); - - IntegerAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public @NonNull Class getItemClass() { - return IIntegerItem.class; - } - - @Override - public IIntegerItem newItem(Object value) { - BigInteger item = toValue(value); - return IIntegerItem.valueOf(item); - } - - @Override - protected IIntegerItem castInternal(@NonNull IAnyAtomicItem item) { - IIntegerItem retval; - if (item instanceof INumericItem) { - retval = newItem(((INumericItem) item).asInteger()); - } else if (item instanceof IBooleanItem) { - boolean value = ((IBooleanItem) item).toBoolean(); - retval = newItem(ObjectUtils.notNull(value ? BigInteger.ONE : BigInteger.ZERO)); - } else { - retval = super.castInternal(item); - } - return retval; - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.math.BigInteger; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class IntegerAdapter + extends AbstractIntegerAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "integer"))); + + IntegerAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public @NonNull Class getItemClass() { + return IIntegerItem.class; + } + + @Override + public IIntegerItem newItem(Object value) { + BigInteger item = toValue(value); + return IIntegerItem.valueOf(item); + } + + @Override + protected IIntegerItem castInternal(@NonNull IAnyAtomicItem item) { + IIntegerItem retval; + if (item instanceof INumericItem) { + retval = newItem(((INumericItem) item).asInteger()); + } else if (item instanceof IBooleanItem) { + boolean value = ((IBooleanItem) item).toBoolean(); + retval = newItem(ObjectUtils.notNull(value ? BigInteger.ONE : BigInteger.ZERO)); + } else { + retval = super.castInternal(item); + } + return retval; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NcNameAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NcNameAdapter.java index 4cb7365a6..4a6c99d26 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NcNameAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NcNameAdapter.java @@ -1,64 +1,67 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.INcNameItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -@Deprecated(forRemoval = true, since = "0.7.0") -public class NcNameAdapter - extends AbstractStringAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("ncname", - // for backwards compatibility with original type name - "NCName")); - - NcNameAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public Class getItemClass() { - return INcNameItem.class; - } - - @Override - public INcNameItem newItem(Object value) { - String item = asString(value); - return INcNameItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INcNameItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +@Deprecated(forRemoval = true, since = "0.7.0") +public class NcNameAdapter + extends AbstractStringAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "ncname"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "NCName"))); + + NcNameAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public Class getItemClass() { + return INcNameItem.class; + } + + @Override + public INcNameItem newItem(Object value) { + String item = asString(value); + return INcNameItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NonNegativeIntegerAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NonNegativeIntegerAdapter.java index 72648415a..23af3d9e3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NonNegativeIntegerAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/NonNegativeIntegerAdapter.java @@ -1,65 +1,68 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.INonNegativeIntegerItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.math.BigInteger; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class NonNegativeIntegerAdapter - extends AbstractIntegerAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "non-negative-integer", - // for backwards compatibility with original type name - "nonNegativeInteger")); - - NonNegativeIntegerAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public Class getItemClass() { - return INonNegativeIntegerItem.class; - } - - @Override - public INonNegativeIntegerItem newItem(Object value) { - BigInteger item = toValue(value); - return INonNegativeIntegerItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INonNegativeIntegerItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.math.BigInteger; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class NonNegativeIntegerAdapter + extends AbstractIntegerAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "non-negative-integer"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "nonNegativeInteger"))); + + NonNegativeIntegerAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public Class getItemClass() { + return INonNegativeIntegerItem.class; + } + + @Override + public INonNegativeIntegerItem newItem(Object value) { + BigInteger item = toValue(value); + return INonNegativeIntegerItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/PositiveIntegerAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/PositiveIntegerAdapter.java index 8e9aad093..3eb2f949d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/PositiveIntegerAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/PositiveIntegerAdapter.java @@ -1,65 +1,68 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IPositiveIntegerItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.math.BigInteger; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class PositiveIntegerAdapter - extends AbstractIntegerAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of( - "positive-integer", - // for backwards compatibility with original type name - "positiveInteger")); - - PositiveIntegerAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public Class getItemClass() { - return IPositiveIntegerItem.class; - } - - @Override - public IPositiveIntegerItem newItem(Object value) { - BigInteger item = toValue(value); - return IPositiveIntegerItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IPositiveIntegerItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.math.BigInteger; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class PositiveIntegerAdapter + extends AbstractIntegerAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of( + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "positive-integer"), + // for backwards compatibility with original type name + new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "positiveInteger"))); + + PositiveIntegerAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public Class getItemClass() { + return IPositiveIntegerItem.class; + } + + @Override + public IPositiveIntegerItem newItem(Object value) { + BigInteger item = toValue(value); + return IPositiveIntegerItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/StringAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/StringAdapter.java index 6d58c700b..e2810e229 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/StringAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/StringAdapter.java @@ -1,61 +1,64 @@ -/* - * 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.datatype.adapter; - -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class StringAdapter - extends AbstractStringAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("string")); - - StringAdapter() { - // avoid general construction - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public Class getItemClass() { - return IStringItem.class; - } - - @Override - public @NonNull IStringItem newItem(@NonNull Object value) { - String item = asString(value); - return IStringItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class StringAdapter + extends AbstractStringAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "string"))); + + StringAdapter() { + // avoid general construction + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public Class getItemClass() { + return IStringItem.class; + } + + @Override + public @NonNull IStringItem newItem(@NonNull Object value) { + String item = asString(value); + return IStringItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/TokenAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/TokenAdapter.java index 327edfe7a..5136d297c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/TokenAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/TokenAdapter.java @@ -26,25 +26,28 @@ package gov.nist.secauto.metaschema.core.datatype.adapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; import gov.nist.secauto.metaschema.core.metapath.item.atomic.ITokenItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class TokenAdapter extends AbstractStringAdapter { @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("token")); + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "token"))); TokenAdapter() { // avoid general construction } @Override - public List getNames() { + public List getNames() { return NAMES; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriAdapter.java index fbfa746ad..f1bb35083 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriAdapter.java @@ -1,83 +1,86 @@ -/* - * Portions of this software was developed by employees of the National Institute - * of Standards and Technology (NIST), an agency of the Federal Government and is - * being made available as a public service. Pursuant to title 17 United States - * Code Section 105, works of NIST employees are not subject to copyright - * protection in the United States. This software may be subject to foreign - * copyright. Permission in the United States and in foreign countries, to the - * extent that NIST may hold copyright, to use, copy, modify, create derivative - * works, and distribute this software and its documentation without fee is hereby - * granted on a non-exclusive basis, provided that this notice and disclaimer - * of warranty appears in all copies. - * - * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER - * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY - * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM - * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE - * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT - * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, - * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, - * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, - * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR - * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT - * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. - */ - -package gov.nist.secauto.metaschema.core.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.net.URI; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class UriAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("uri")); - - UriAdapter() { - super(URI.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public URI parse(String value) { - return URI.create(value); - } - - @Override - public URI copy(Object obj) { - // a URI is immutable - return (URI) obj; - } - - @Override - public Class getItemClass() { - return IAnyUriItem.class; - } - - @Override - public IAnyUriItem newItem(Object value) { - URI item = toValue(value); - return IAnyUriItem.valueOf(item); - } - -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.net.URI; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class UriAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "uri"))); + + UriAdapter() { + super(URI.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public URI parse(String value) { + return URI.create(value); + } + + @Override + public URI copy(Object obj) { + // a URI is immutable + return (URI) obj; + } + + @Override + public Class getItemClass() { + return IAnyUriItem.class; + } + + @Override + public IAnyUriItem newItem(Object value) { + URI item = toValue(value); + return IAnyUriItem.valueOf(item); + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriReferenceAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriReferenceAdapter.java index e17f4d58b..2e3015bcc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriReferenceAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UriReferenceAdapter.java @@ -1,82 +1,85 @@ -/* - * 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.datatype.adapter; - -import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; - -import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUriReferenceItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import java.net.URI; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class UriReferenceAdapter - extends AbstractDataTypeAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("uri-reference")); - - UriReferenceAdapter() { - super(URI.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public JsonFormatTypes getJsonRawType() { - return JsonFormatTypes.STRING; - } - - @SuppressWarnings("null") - @Override - public URI parse(String value) { - return URI.create(value); - } - - @Override - public URI copy(Object obj) { - // a URI is immutable - return (URI) obj; - } - - @Override - public Class getItemClass() { - return IUriReferenceItem.class; - } - - @Override - public IUriReferenceItem newItem(Object value) { - URI item = toValue(value); - return IUriReferenceItem.valueOf(item); - } -} +/* + * 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.datatype.adapter; + +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; + +import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUriReferenceItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.net.URI; +import java.util.List; + +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class UriReferenceAdapter + extends AbstractDataTypeAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "uri-reference"))); + + UriReferenceAdapter() { + super(URI.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public JsonFormatTypes getJsonRawType() { + return JsonFormatTypes.STRING; + } + + @SuppressWarnings("null") + @Override + public URI parse(String value) { + return URI.create(value); + } + + @Override + public URI copy(Object obj) { + // a URI is immutable + return (URI) obj; + } + + @Override + public Class getItemClass() { + return IUriReferenceItem.class; + } + + @Override + public IUriReferenceItem newItem(Object value) { + URI item = toValue(value); + return IUriReferenceItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UuidAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UuidAdapter.java index fda5a1234..7338723b5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UuidAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/UuidAdapter.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUuidItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -36,13 +37,15 @@ import java.util.UUID; import java.util.regex.Pattern; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class UuidAdapter extends AbstractDataTypeAdapter { @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("uuid")); + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "uuid"))); public static final Pattern UUID_PATTERN = Pattern.compile("^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[45][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$"); @@ -51,7 +54,7 @@ public class UuidAdapter } @Override - public List getNames() { + public List getNames() { return NAMES; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/YearMonthAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/YearMonthAdapter.java index 0d9190696..b1fcff918 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/YearMonthAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/YearMonthAdapter.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import gov.nist.secauto.metaschema.core.datatype.AbstractDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -36,20 +37,22 @@ import java.time.format.DateTimeParseException; import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class YearMonthAdapter extends AbstractDataTypeAdapter { @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("year-month-duration")); + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "year-month-duration"))); YearMonthAdapter() { super(Period.class); } @Override - public List getNames() { + public List getNames() { return NAMES; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupString.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupString.java index 667e292af..bef3a08e7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupString.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupString.java @@ -1,300 +1,300 @@ -/* - * 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.datatype.markup; - -import com.ctc.wstx.api.WstxOutputProperties; -import com.ctc.wstx.stax.WstxOutputFactory; -import com.vladsch.flexmark.formatter.Formatter; -import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; -import com.vladsch.flexmark.parser.Parser; -import com.vladsch.flexmark.util.ast.Document; -import com.vladsch.flexmark.util.ast.Node; - -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.AstCollectingVisitor; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.IMarkupVisitor; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.IMarkupWriter; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertVisitor; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupVisitor; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupXmlEventWriter; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupXmlStreamWriter; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.codehaus.stax2.XMLOutputFactory2; -import org.codehaus.stax2.XMLStreamWriter2; -import org.codehaus.stax2.evt.XMLEventFactory2; -import org.jsoup.Jsoup; -import org.jsoup.nodes.TextNode; -import org.jsoup.select.NodeTraversor; -import org.jsoup.select.NodeVisitor; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.function.Predicate; -import java.util.regex.Pattern; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import javax.xml.stream.XMLEventWriter; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; - -@SuppressWarnings("PMD.CouplingBetweenObjects") -public abstract class AbstractMarkupString> - implements IMarkupString { - private static final Logger LOGGER = LogManager.getLogger(FlexmarkFactory.class); - - private static final Pattern QUOTE_TAG_REPLACEMENT_PATTERN - = Pattern.compile(""); - - // - // @NonNull - // private static final String DEFAULT_HTML_NS = "http://www.w3.org/1999/xhtml"; - // @NonNull - // private static final String DEFAULT_HTML_PREFIX = ""; - - @NonNull - private final Document document; - - /** - * Construct a new markup string based on the provided flexmark AST graph. - * - * @param document - * the AST graph representing Markdown text - */ - protected AbstractMarkupString(@NonNull Document document) { - this.document = document; - } - - @Override - public Document getDocument() { - return document; - } - - @Override - public boolean isEmpty() { - return getDocument().getFirstChild() == null; - } - - /** - * Parse HTML-based text into markdown as a flexmark AST graph. - *

- * This method uses a two-step approach that first translates the HTML into - * markdown, and then parses the markdown into an AST graph. - * - * @param html - * the HTML text to parse - * @param htmlParser - * the HTML parser used to produce markdown - * @param markdownParser - * the markdown parser - * @return the markdown AST graph - */ - @NonNull - protected static Document parseHtml(@NonNull String html, @NonNull FlexmarkHtmlConverter htmlParser, - @NonNull Parser markdownParser) { - org.jsoup.nodes.Document document = Jsoup.parse(html); - - // Fix for usnistgov/liboscal-java#5 - // Caused by not stripping out extra newlines inside HTML tags - NodeTraversor.traverse(new NodeVisitor() { - - @Override - public void head(org.jsoup.nodes.Node node, int depth) { - if (node instanceof TextNode) { - TextNode textNode = (TextNode) node; - - org.jsoup.nodes.Node parent = textNode.parent(); - - if (!isTag(parent, "code") || !isTag(parent.parent(), "pre")) { - node.replaceWith(new TextNode(textNode.text())); - } - } - } - - private boolean isTag(@Nullable org.jsoup.nodes.Node node, @NonNull String tagName) { - return node != null && tagName.equals(node.normalName()); - } - - }, document); - - String markdown = htmlParser.convert(document); - assert markdown != null; - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("html->markdown: {}", markdown); - } - return parseMarkdown(markdown, markdownParser); - } - - /** - * Parse markdown-based text into a flexmark AST graph. - * - * @param markdown - * the markdown text to parse - * @param parser - * the markdown parser - * @return the markdown AST graph - */ - @SuppressWarnings("null") - @NonNull - protected static Document parseMarkdown(@NonNull String markdown, @NonNull Parser parser) { - return parser.parse(markdown); - } - - @Override - public String toXHtml(@NonNull String namespace) throws XMLStreamException, IOException { - - String retval; - - Document document = getDocument(); - if (document.hasChildren()) { - - XMLOutputFactory2 factory = (XMLOutputFactory2) XMLOutputFactory.newInstance(); - assert factory instanceof WstxOutputFactory; - factory.setProperty(WstxOutputProperties.P_OUTPUT_VALIDATE_STRUCTURE, false); - try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - XMLStreamWriter2 xmlStreamWriter = (XMLStreamWriter2) factory.createXMLStreamWriter(os); - - writeXHtml(namespace, ObjectUtils.notNull(xmlStreamWriter)); - - xmlStreamWriter.flush(); - xmlStreamWriter.close(); - os.flush(); - retval = ObjectUtils.notNull(os.toString(StandardCharsets.UTF_8)); - } - } else { - retval = ""; - } - return retval; - } - - @Override - public String toHtml() { - // String html; - // try { - // html = toXHtml(""); - // } catch(RuntimeException ex) { - // throw ex; - // } catch (Throwable ex) { - // throw new RuntimeException(ex); - // } - // return QUOTE_TAG_REPLACEMENT_PATTERN.matcher(html) - // .replaceAll("""); - String html = getFlexmarkFactory().getHtmlRenderer().render(getDocument()); - return ObjectUtils.notNull(QUOTE_TAG_REPLACEMENT_PATTERN.matcher(html) - .replaceAll(""")); - } - - @Override - public String toMarkdown() { - return toMarkdown(getFlexmarkFactory().getFormatter()); - } - - @Override - public String toMarkdown(Formatter formatter) { - return ObjectUtils.notNull(formatter.render(getDocument())); - } - - @Override - public void writeXHtml(String namespace, XMLStreamWriter2 streamWriter) throws XMLStreamException { - Document document = getDocument(); - if (document.hasChildren()) { - IMarkupWriter writer = new MarkupXmlStreamWriter( - namespace, - getFlexmarkFactory().getListOptions(), - streamWriter); - - IMarkupVisitor visitor = new MarkupVisitor<>(isBlock()); - visitor.visitDocument(document, writer); - } else { - streamWriter.writeCharacters(""); - } - } - - @Override - public void writeXHtml(String namespace, XMLEventFactory2 eventFactory, XMLEventWriter eventWriter) - throws XMLStreamException { - Document document = getDocument(); - if (document.hasChildren()) { - - IMarkupWriter writer = new MarkupXmlEventWriter( - namespace, - getFlexmarkFactory().getListOptions(), - eventWriter, - eventFactory); - - IMarkupVisitor visitor = new MarkupVisitor<>(isBlock()); - visitor.visitDocument(getDocument(), writer); - } else { - eventWriter.add(eventFactory.createSpace("")); - } - - } - - @SuppressWarnings("null") - @Override - public Stream getNodesAsStream() { - return Stream.concat(Stream.of(getDocument()), - StreamSupport.stream(getDocument().getDescendants().spliterator(), false)); - } - - @Override - @NonNull - public List getInserts() { - return getInserts(insert -> true); - } - - /** - * Retrieve all insert statements that are contained within this markup text - * that match the provided filter. - * - * @param filter - * a filter used to identify matching insert statements - * @return the matching insert statements - */ - @Override - @NonNull - public List getInserts(@NonNull Predicate filter) { - InsertVisitor visitor = new InsertVisitor(filter); - visitor.visitChildren(getDocument()); - return visitor.getInserts(); - } - - @Override - public String toString() { - return AstCollectingVisitor.asString(getDocument()); - } -} +/* + * 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.datatype.markup; + +import com.ctc.wstx.api.WstxOutputProperties; +import com.ctc.wstx.stax.WstxOutputFactory; +import com.vladsch.flexmark.formatter.Formatter; +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; + +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.AstCollectingVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.IMarkupVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.IMarkupWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupXmlEventWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.MarkupXmlStreamWriter; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.codehaus.stax2.XMLOutputFactory2; +import org.codehaus.stax2.XMLStreamWriter2; +import org.codehaus.stax2.evt.XMLEventFactory2; +import org.jsoup.Jsoup; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +@SuppressWarnings("PMD.CouplingBetweenObjects") +public abstract class AbstractMarkupString> + implements IMarkupString { + private static final Logger LOGGER = LogManager.getLogger(FlexmarkFactory.class); + + private static final Pattern QUOTE_TAG_REPLACEMENT_PATTERN + = Pattern.compile(""); + + // + // @NonNull + // private static final String DEFAULT_HTML_NS = "http://www.w3.org/1999/xhtml"; + // @NonNull + // private static final String DEFAULT_HTML_PREFIX = ""; + + @NonNull + private final Document document; + + /** + * Construct a new markup string based on the provided flexmark AST graph. + * + * @param document + * the AST graph representing Markdown text + */ + protected AbstractMarkupString(@NonNull Document document) { + this.document = document; + } + + @Override + public Document getDocument() { + return document; + } + + @Override + public boolean isEmpty() { + return getDocument().getFirstChild() == null; + } + + /** + * Parse HTML-based text into markdown as a flexmark AST graph. + *

+ * This method uses a two-step approach that first translates the HTML into + * markdown, and then parses the markdown into an AST graph. + * + * @param html + * the HTML text to parse + * @param htmlParser + * the HTML parser used to produce markdown + * @param markdownParser + * the markdown parser + * @return the markdown AST graph + */ + @NonNull + protected static Document parseHtml(@NonNull String html, @NonNull FlexmarkHtmlConverter htmlParser, + @NonNull Parser markdownParser) { + org.jsoup.nodes.Document document = Jsoup.parse(html); + + // Fix for usnistgov/liboscal-java#5 + // Caused by not stripping out extra newlines inside HTML tags + NodeTraversor.traverse(new NodeVisitor() { + + @Override + public void head(org.jsoup.nodes.Node node, int depth) { + if (node instanceof TextNode) { + TextNode textNode = (TextNode) node; + + org.jsoup.nodes.Node parent = textNode.parent(); + + if (!isTag(parent, "code") || !isTag(parent.parent(), "pre")) { + node.replaceWith(new TextNode(textNode.text())); + } + } + } + + private boolean isTag(@Nullable org.jsoup.nodes.Node node, @NonNull String tagName) { + return node != null && tagName.equals(node.normalName()); + } + + }, document); + + String markdown = htmlParser.convert(document); + assert markdown != null; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("html->markdown: {}", markdown); + } + return parseMarkdown(markdown, markdownParser); + } + + /** + * Parse markdown-based text into a flexmark AST graph. + * + * @param markdown + * the markdown text to parse + * @param parser + * the markdown parser + * @return the markdown AST graph + */ + @SuppressWarnings("null") + @NonNull + protected static Document parseMarkdown(@NonNull String markdown, @NonNull Parser parser) { + return parser.parse(markdown); + } + + @Override + public String toXHtml(@NonNull String namespace) throws XMLStreamException, IOException { + + String retval; + + Document document = getDocument(); + if (document.hasChildren()) { + + XMLOutputFactory2 factory = (XMLOutputFactory2) XMLOutputFactory.newInstance(); + assert factory instanceof WstxOutputFactory; + factory.setProperty(WstxOutputProperties.P_OUTPUT_VALIDATE_STRUCTURE, false); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + XMLStreamWriter2 xmlStreamWriter = (XMLStreamWriter2) factory.createXMLStreamWriter(os); + + writeXHtml(namespace, ObjectUtils.notNull(xmlStreamWriter)); + + xmlStreamWriter.flush(); + xmlStreamWriter.close(); + os.flush(); + retval = ObjectUtils.notNull(os.toString(StandardCharsets.UTF_8)); + } + } else { + retval = ""; + } + return retval; + } + + @Override + public String toHtml() { + // String html; + // try { + // html = toXHtml(""); + // } catch(RuntimeException ex) { + // throw ex; + // } catch (Throwable ex) { + // throw new RuntimeException(ex); + // } + // return QUOTE_TAG_REPLACEMENT_PATTERN.matcher(html) + // .replaceAll("""); + String html = getFlexmarkFactory().getHtmlRenderer().render(getDocument()); + return ObjectUtils.notNull(QUOTE_TAG_REPLACEMENT_PATTERN.matcher(html) + .replaceAll(""")); + } + + @Override + public String toMarkdown() { + return toMarkdown(getFlexmarkFactory().getFormatter()); + } + + @Override + public String toMarkdown(Formatter formatter) { + return ObjectUtils.notNull(formatter.render(getDocument())); + } + + @Override + public void writeXHtml(String namespace, XMLStreamWriter2 streamWriter) throws XMLStreamException { + Document document = getDocument(); + if (document.hasChildren()) { + IMarkupWriter writer = new MarkupXmlStreamWriter( + namespace, + getFlexmarkFactory().getListOptions(), + streamWriter); + + IMarkupVisitor visitor = new MarkupVisitor<>(isBlock()); + visitor.visitDocument(document, writer); + } else { + streamWriter.writeCharacters(""); + } + } + + @Override + public void writeXHtml(String namespace, XMLEventFactory2 eventFactory, XMLEventWriter eventWriter) + throws XMLStreamException { + Document document = getDocument(); + if (document.hasChildren()) { + + IMarkupWriter writer = new MarkupXmlEventWriter( + namespace, + getFlexmarkFactory().getListOptions(), + eventWriter, + eventFactory); + + IMarkupVisitor visitor = new MarkupVisitor<>(isBlock()); + visitor.visitDocument(getDocument(), writer); + } else { + eventWriter.add(eventFactory.createSpace("")); + } + + } + + @SuppressWarnings("null") + @Override + public Stream getNodesAsStream() { + return Stream.concat(Stream.of(getDocument()), + StreamSupport.stream(getDocument().getDescendants().spliterator(), false)); + } + + @Override + @NonNull + public List getInserts() { + return getInserts(insert -> true); + } + + /** + * Retrieve all insert statements that are contained within this markup text + * that match the provided filter. + * + * @param filter + * a filter used to identify matching insert statements + * @return the matching insert statements + */ + @Override + @NonNull + public List getInserts(@NonNull Predicate filter) { + InsertAnchorExtension.InsertVisitor visitor = new InsertAnchorExtension.InsertVisitor(filter); + visitor.visitChildren(getDocument()); + return visitor.getInserts(); + } + + @Override + public String toString() { + return AstCollectingVisitor.asString(getDocument()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/IMarkupString.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/IMarkupString.java index a2d162b56..9e97a1236 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/IMarkupString.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/IMarkupString.java @@ -49,12 +49,27 @@ public interface IMarkupString> extends ICustomJavaDataType { + /** + * Get the underlying Flexmark factory supporting markup serialization. + * + * @return the factory + */ @NonNull FlexmarkFactory getFlexmarkFactory(); + /** + * Get the top-level Flexmark document node for the markup. + * + * @return the node + */ @NonNull Document getDocument(); + /** + * Determine if the markup has no contents. + * + * @return {@code true} if the markup has no contents or {@code false} otherwise + */ boolean isEmpty(); // /** @@ -73,15 +88,49 @@ public interface IMarkupString> // throws // XMLStreamException; + /** + * Get the HyperText Markup Language (HTML) representation of this markup + * string. + * + * @return the HTML + */ @NonNull String toHtml(); + /** + * Get the Extensible HyperText Markup Language (XHTML) representation of this + * markup string. + * + * @param namespace + * the XML namespace to use for XHTML elements + * + * @return the XHTML + * @throws XMLStreamException + * if an error occurred while establishing or writing to the + * underlying XML stream + * @throws IOException + * if an error occurred while generating the XHTML data + */ @NonNull String toXHtml(@NonNull String namespace) throws XMLStreamException, IOException; + /** + * Get the Commonmark Markdown representation of this markup string. + * + * @return the Markdown + */ @NonNull String toMarkdown(); + /** + * Get a Markdown representation of this markup string, which will be created by + * the provided formatter. + * + * @param formatter + * the specific Markdown formatter to use in producing the Markdown + * + * @return the Markdown + */ @NonNull String toMarkdown(@NonNull Formatter formatter); @@ -93,6 +142,11 @@ public interface IMarkupString> @NonNull Stream getNodesAsStream(); + /** + * Get markup inserts used as place holders within the string. + * + * @return a list of insets or an empty list if no inserts are present + */ @NonNull default List getInserts() { return getInserts(insert -> true); @@ -118,10 +172,37 @@ List getInserts( */ boolean isBlock(); + /** + * Write the Extensible HyperText Markup Language (XHTML) representation of this + * markup string to the provided stream writer. + * + * @param namespace + * the XML namespace to use for XHTML elements + * @param streamWriter + * the XML stream to write to + * @throws XMLStreamException + * if an error occurred while establishing or writing to the XML + * stream + */ void writeXHtml( @NonNull String namespace, @NonNull XMLStreamWriter2 streamWriter) throws XMLStreamException; + /** + * Write the Extensible HyperText Markup Language (XHTML) representation of this + * markup string to the provided stream writer using the provided XML event + * factory. + * + * @param namespace + * the XML namespace to use for XHTML elements + * @param eventFactory + * the XML event factory used to generate XML events to write + * @param eventWriter + * the XML event stream to write to + * @throws XMLStreamException + * if an error occurred while establishing or writing to the XML + * stream + */ void writeXHtml( @NonNull String namespace, @NonNull XMLEventFactory2 eventFactory, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java index 20772f991..eac3f871b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLine.java @@ -1,110 +1,130 @@ -/* - * 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.datatype.markup; - -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.parser.Parser; -import com.vladsch.flexmark.util.ast.Block; -import com.vladsch.flexmark.util.ast.Document; -import com.vladsch.flexmark.util.ast.Node; -import com.vladsch.flexmark.util.data.DataSet; -import com.vladsch.flexmark.util.data.MutableDataSet; -import com.vladsch.flexmark.util.misc.Extension; - -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkConfiguration; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.SuppressPTagExtension; - -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class MarkupLine - extends AbstractMarkupString { - - @NonNull - private static final DataSet FLEXMARK_CONFIG = newParserOptions(); - - @NonNull - private static final FlexmarkFactory FLEXMARK_FACTORY = FlexmarkFactory.newInstance(FLEXMARK_CONFIG); - - @SuppressWarnings("null") - @NonNull - protected static DataSet newParserOptions() { - MutableDataSet options = new MutableDataSet(); - // disable inline HTML - options.set(Parser.HTML_BLOCK_PARSER, false); - // disable list processing - options.set(Parser.LIST_BLOCK_PARSER, false); - options.set(HtmlRenderer.SUPPRESS_HTML_BLOCKS, true); - - Collection currentExtensions = Parser.EXTENSIONS.get(FlexmarkConfiguration.FLEXMARK_CONFIG); - List extensions = new LinkedList<>(currentExtensions); - extensions.add(SuppressPTagExtension.create()); - Parser.EXTENSIONS.set(options, extensions); - - return FlexmarkConfiguration.newFlexmarkConfig(options); - } - - @NonNull - public static MarkupLine fromHtml(@NonNull String html) { - return new MarkupLine( - parseHtml(html, FLEXMARK_FACTORY.getFlexmarkHtmlConverter(), FLEXMARK_FACTORY.getMarkdownParser())); - } - - @NonNull - public static MarkupLine fromMarkdown(@NonNull String markdown) { - return new MarkupLine(parseMarkdown(markdown, FLEXMARK_FACTORY.getMarkdownParser())); - } - - @Override - public FlexmarkFactory getFlexmarkFactory() { - return FLEXMARK_FACTORY; - } - - protected MarkupLine(@NonNull Document astNode) { - super(astNode); - Node child = astNode.getFirstChild(); - if (child instanceof Block && child.getNext() != null) { - throw new IllegalStateException("multiple blocks not allowed"); - } // else empty markdown - } - - @Override - public MarkupLine copy() { - // TODO: find a way to do a deep copy - // this is a shallow copy that uses the same underlying Document object - return new MarkupLine(getDocument()); - } - - @Override - public boolean isBlock() { - return false; - } -} +/* + * 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.datatype.markup; + +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Block; +import com.vladsch.flexmark.util.ast.Document; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.data.DataSet; +import com.vladsch.flexmark.util.data.MutableDataSet; +import com.vladsch.flexmark.util.misc.Extension; + +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkConfiguration; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.SuppressPTagExtension; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MarkupLine + extends AbstractMarkupString { + + @NonNull + private static final DataSet FLEXMARK_CONFIG = newParserOptions(); + + @NonNull + private static final FlexmarkFactory FLEXMARK_FACTORY = FlexmarkFactory.newInstance(FLEXMARK_CONFIG); + + @SuppressWarnings("null") + @NonNull + private static DataSet newParserOptions() { + MutableDataSet options = new MutableDataSet(); + // disable inline HTML + options.set(Parser.HTML_BLOCK_PARSER, false); + // disable list processing + options.set(Parser.LIST_BLOCK_PARSER, false); + options.set(HtmlRenderer.SUPPRESS_HTML_BLOCKS, true); + + Collection currentExtensions = Parser.EXTENSIONS.get(FlexmarkConfiguration.FLEXMARK_CONFIG); + List extensions = new LinkedList<>(currentExtensions); + extensions.add(SuppressPTagExtension.newInstance()); + Parser.EXTENSIONS.set(options, extensions); + + return FlexmarkConfiguration.newFlexmarkConfig(options); + } + + /** + * Convert the provided HTML string into markup. + * + * @param html + * the HTML + * @return the markup instance + */ + @NonNull + public static MarkupLine fromHtml(@NonNull String html) { + return new MarkupLine( + parseHtml(html, FLEXMARK_FACTORY.getFlexmarkHtmlConverter(), FLEXMARK_FACTORY.getMarkdownParser())); + } + + /** + * Convert the provided markdown string into markup. + * + * @param markdown + * the markup + * @return the markup instance + */ + @NonNull + public static MarkupLine fromMarkdown(@NonNull String markdown) { + return new MarkupLine(parseMarkdown(markdown, FLEXMARK_FACTORY.getMarkdownParser())); + } + + @Override + public FlexmarkFactory getFlexmarkFactory() { + return FLEXMARK_FACTORY; + } + + /** + * Construct a new single line markup instance. + * + * @param astNode + * the parsed markup AST + */ + protected MarkupLine(@NonNull Document astNode) { + super(astNode); + Node child = astNode.getFirstChild(); + if (child instanceof Block && child.getNext() != null) { + throw new IllegalStateException("multiple blocks not allowed"); + } // else empty markdown + } + + @Override + public MarkupLine copy() { + // TODO: find a way to do a deep copy + // this is a shallow copy that uses the same underlying Document object + return new MarkupLine(getDocument()); + } + + @Override + public boolean isBlock() { + return false; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLineAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLineAdapter.java index b1dbdd58b..0e3d0b31a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLineAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupLineAdapter.java @@ -1,100 +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.datatype.markup; - -import com.fasterxml.jackson.core.JsonParser; - -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.XmlMarkupParser; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.codehaus.stax2.XMLEventReader2; - -import java.io.IOException; -import java.util.List; - -import javax.xml.stream.XMLStreamException; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class MarkupLineAdapter - extends AbstractMarkupAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("markup-line")); - - MarkupLineAdapter() { - super(MarkupLine.class); - } - - @Override - public List getNames() { - return NAMES; - } - - /** - * Parse a line of Markdown. - */ - @Override - public MarkupLine parse(String value) { - return MarkupLine.fromMarkdown(value); - } - - @SuppressWarnings("null") - @Override - public MarkupLine parse(XMLEventReader2 eventReader) throws IOException { - try { - return XmlMarkupParser.instance().parseMarkupline(eventReader); - } catch (XMLStreamException ex) { - throw new IOException(ex); - } - } - - @Override - public MarkupLine parse(JsonParser parser) throws IOException { - @SuppressWarnings("null") MarkupLine retval = parse(parser.getValueAsString()); - // skip past value - parser.nextToken(); - return retval; - } - - @Override - public String getDefaultJsonValueKey() { - return "RICHTEXT"; - } - - @Override - public Class getItemClass() { - return IMarkupItem.class; - } - - @Override - public IMarkupItem newItem(Object value) { - MarkupLine item = toValue(value); - return IMarkupItem.valueOf(item); - } -} +/* + * 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.datatype.markup; + +import com.fasterxml.jackson.core.JsonParser; + +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.XmlMarkupParser; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.codehaus.stax2.XMLEventReader2; + +import java.io.IOException; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class MarkupLineAdapter + extends AbstractMarkupAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "markup-line"))); + + MarkupLineAdapter() { + super(MarkupLine.class); + } + + @Override + public List getNames() { + return NAMES; + } + + /** + * Parse a line of Markdown. + */ + @Override + public MarkupLine parse(String value) { + return MarkupLine.fromMarkdown(value); + } + + @SuppressWarnings("null") + @Override + public MarkupLine parse(XMLEventReader2 eventReader) throws IOException { + try { + return XmlMarkupParser.instance().parseMarkupline(eventReader); + } catch (XMLStreamException ex) { + throw new IOException(ex); + } + } + + @Override + public MarkupLine parse(JsonParser parser) throws IOException { + @SuppressWarnings("null") MarkupLine retval = parse(parser.getValueAsString()); + // skip past value + parser.nextToken(); + return retval; + } + + @Override + public String getDefaultJsonValueKey() { + return "RICHTEXT"; + } + + @Override + public Class getItemClass() { + return IMarkupItem.class; + } + + @Override + public IMarkupItem newItem(Object value) { + MarkupLine item = toValue(value); + return IMarkupItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultiline.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultiline.java index 205b0ff35..dbcb2236b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultiline.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultiline.java @@ -1,96 +1,96 @@ -/* - * 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.datatype.markup; - -import com.vladsch.flexmark.util.ast.Document; - -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class MarkupMultiline - extends AbstractMarkupString { - - @NonNull - private static final FlexmarkFactory FLEXMARK_FACTORY = FlexmarkFactory.instance(); - - /** - * Convert the provided HTML string into markup. - * - * @param html - * the HTML - * @return the multiline markup instance - */ - @NonNull - public static MarkupMultiline fromHtml(@NonNull String html) { - return new MarkupMultiline( - parseHtml( - html, - FLEXMARK_FACTORY.getFlexmarkHtmlConverter(), - FLEXMARK_FACTORY.getMarkdownParser())); - } - - /** - * Convert the provided markdown string into markup. - * - * @param markdown - * the markup - * @return the multiline markup instance - */ - @NonNull - public static MarkupMultiline fromMarkdown(@NonNull String markdown) { - return new MarkupMultiline( - parseMarkdown(markdown, FLEXMARK_FACTORY.getMarkdownParser())); - } - - /** - * Construct a new multiline markup instance. - * - * @param astNode - * the parsed markup AST - */ - public MarkupMultiline(@NonNull Document astNode) { - super(astNode); - } - - @Override - public FlexmarkFactory getFlexmarkFactory() { - return FLEXMARK_FACTORY; - } - - @Override - public MarkupMultiline copy() { - // TODO: find a way to do a deep copy - // this is a shallow copy that uses the same underlying Document object - return new MarkupMultiline(getDocument()); - } - - @Override - public boolean isBlock() { - return true; - } -} +/* + * 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.datatype.markup; + +import com.vladsch.flexmark.util.ast.Document; + +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.FlexmarkFactory; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class MarkupMultiline + extends AbstractMarkupString { + + @NonNull + private static final FlexmarkFactory FLEXMARK_FACTORY = FlexmarkFactory.instance(); + + /** + * Convert the provided HTML string into markup. + * + * @param html + * the HTML + * @return the markup instance + */ + @NonNull + public static MarkupMultiline fromHtml(@NonNull String html) { + return new MarkupMultiline( + parseHtml( + html, + FLEXMARK_FACTORY.getFlexmarkHtmlConverter(), + FLEXMARK_FACTORY.getMarkdownParser())); + } + + /** + * Convert the provided markdown string into markup. + * + * @param markdown + * the markup + * @return the markup instance + */ + @NonNull + public static MarkupMultiline fromMarkdown(@NonNull String markdown) { + return new MarkupMultiline( + parseMarkdown(markdown, FLEXMARK_FACTORY.getMarkdownParser())); + } + + /** + * Construct a new multiline markup instance. + * + * @param astNode + * the parsed markup AST + */ + public MarkupMultiline(@NonNull Document astNode) { + super(astNode); + } + + @Override + public FlexmarkFactory getFlexmarkFactory() { + return FLEXMARK_FACTORY; + } + + @Override + public MarkupMultiline copy() { + // TODO: find a way to do a deep copy + // this is a shallow copy that uses the same underlying Document object + return new MarkupMultiline(getDocument()); + } + + @Override + public boolean isBlock() { + return true; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultilineAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultilineAdapter.java index 3c907fa90..fee505840 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultilineAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupMultilineAdapter.java @@ -1,111 +1,112 @@ -/* - * 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.datatype.markup; - -import com.fasterxml.jackson.core.JsonParser; - -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.XmlMarkupParser; -import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.codehaus.stax2.XMLEventReader2; - -import java.io.IOException; -import java.util.List; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamException; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class MarkupMultilineAdapter - extends AbstractMarkupAdapter { - @NonNull - private static final List NAMES = ObjectUtils.notNull( - List.of("markup-multiline")); - - MarkupMultilineAdapter() { - super(MarkupMultiline.class); - } - - @Override - public List getNames() { - return NAMES; - } - - @Override - public boolean isUnrappedValueAllowedInXml() { - return true; - } - - /** - * Parse a line of Markdown. - */ - @Override - public MarkupMultiline parse(String value) { - return MarkupMultiline.fromMarkdown(value); - } - - @SuppressWarnings("null") - @Override - public MarkupMultiline parse(XMLEventReader2 eventReader) throws IOException { - try { - return XmlMarkupParser.instance().parseMarkupMultiline(eventReader); - } catch (XMLStreamException ex) { - throw new IOException(ex); - } - } - - @Override - public MarkupMultiline parse(JsonParser parser) throws IOException { - @SuppressWarnings("null") MarkupMultiline retval = parse(parser.getValueAsString()); - // skip past value - parser.nextToken(); - return retval; - } - - @Override - public boolean canHandleQName(QName nextQName) { - return true; - } - - @Override - public String getDefaultJsonValueKey() { - return "PROSE"; - } - - @Override - public Class getItemClass() { - return IMarkupItem.class; - } - - @Override - public IMarkupItem newItem(Object value) { - MarkupMultiline item = toValue(value); - return IMarkupItem.valueOf(item); - } -} +/* + * 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.datatype.markup; + +import com.fasterxml.jackson.core.JsonParser; + +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.XmlMarkupParser; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.codehaus.stax2.XMLEventReader2; + +import java.io.IOException; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class MarkupMultilineAdapter + extends AbstractMarkupAdapter { + @NonNull + private static final List NAMES = ObjectUtils.notNull( + List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "markup-multiline"))); + + MarkupMultilineAdapter() { + super(MarkupMultiline.class); + } + + @Override + public List getNames() { + return NAMES; + } + + @Override + public boolean isUnrappedValueAllowedInXml() { + return true; + } + + /** + * Parse a line of Markdown. + */ + @Override + public MarkupMultiline parse(String value) { + return MarkupMultiline.fromMarkdown(value); + } + + @SuppressWarnings("null") + @Override + public MarkupMultiline parse(XMLEventReader2 eventReader) throws IOException { + try { + return XmlMarkupParser.instance().parseMarkupMultiline(eventReader); + } catch (XMLStreamException ex) { + throw new IOException(ex); + } + } + + @Override + public MarkupMultiline parse(JsonParser parser) throws IOException { + @SuppressWarnings("null") MarkupMultiline retval = parse(parser.getValueAsString()); + // skip past value + parser.nextToken(); + return retval; + } + + @Override + public boolean canHandleQName(QName nextQName) { + return true; + } + + @Override + public String getDefaultJsonValueKey() { + return "PROSE"; + } + + @Override + public Class getItemClass() { + return IMarkupItem.class; + } + + @Override + public IMarkupItem newItem(Object value) { + MarkupMultiline item = toValue(value); + return IMarkupItem.valueOf(item); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AstCollectingVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AstCollectingVisitor.java index 2d51a98e2..be57b01bc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AstCollectingVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AstCollectingVisitor.java @@ -1,88 +1,88 @@ -/* - * 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.datatype.markup.flexmark; - -import com.vladsch.flexmark.util.ast.Node; -import com.vladsch.flexmark.util.ast.NodeVisitorBase; - -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public final class AstCollectingVisitor - extends NodeVisitorBase { - public static final String EOL = "\n"; - - @SuppressWarnings("PMD.AvoidStringBufferField") // short lived - private final StringBuilder strBuilder; - private int indent; // 0; - - private AstCollectingVisitor(@NonNull StringBuilder strBuilder) { - this.strBuilder = strBuilder; - indent = 0; - } - - /** - * Generate a string representation of an AST. - * - * @param node - * the branch of the tree to visualize - * @return the string representation of the AST. - */ - @NonNull - public static String asString(@NonNull Node node) { - StringBuilder builder = new StringBuilder(); - AstCollectingVisitor visitor = new AstCollectingVisitor(builder); - visitor.collect(node); - return ObjectUtils.notNull(builder.toString()); - } - - private void appendIndent() { - for (int i = 0; i < indent * 2; i++) { - strBuilder.append(' '); - } - } - - private void collect(@NonNull Node node) { - visit(node); - } - - @Override - protected void visit(Node node) { - assert node != null; - appendIndent(); - node.astString(strBuilder, true); - strBuilder.append(EOL); - indent++; - - try { - super.visitChildren(node); - } finally { - indent--; - } - } -} +/* + * 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.datatype.markup.flexmark; + +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.NodeVisitorBase; + +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class AstCollectingVisitor + extends NodeVisitorBase { + public static final String EOL = "\n"; + + @SuppressWarnings("PMD.AvoidStringBufferField") // short lived + private final StringBuilder strBuilder; + private int indent; // 0; + + private AstCollectingVisitor(@NonNull StringBuilder strBuilder) { + this.strBuilder = strBuilder; + indent = 0; + } + + /** + * Generate a string representation of an AST. + * + * @param node + * the branch of the tree to visualize + * @return the string representation of the AST. + */ + @NonNull + public static String asString(@NonNull Node node) { + StringBuilder builder = new StringBuilder(); + AstCollectingVisitor visitor = new AstCollectingVisitor(builder); + visitor.collect(node); + return ObjectUtils.notNull(builder.toString()); + } + + private void appendIndent() { + for (int i = 0; i < indent * 2; i++) { + strBuilder.append(' '); + } + } + + private void collect(@NonNull Node node) { + visit(node); + } + + @Override + protected void visit(Node node) { + assert node != null; + appendIndent(); + node.astString(strBuilder, true); + strBuilder.append(EOL); + indent++; + + try { + super.visitChildren(node); + } finally { + indent--; + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkConfiguration.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkConfiguration.java index 7c1c4b005..2c8bf7310 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkConfiguration.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkConfiguration.java @@ -65,13 +65,13 @@ private static DataSet initFlexmarkConfig() { List extensions = List.of( // Metaschema insert - InsertAnchorExtension.create(), + InsertAnchorExtension.newInstance(), // q tag handling - HtmlQuoteTagExtension.create(), + HtmlQuoteTagExtension.newInstance(), TypographicExtension.create(), TablesExtension.create(), // fix for code handling - HtmlCodeRenderExtension.create(), + HtmlCodeRenderExtension.newInstance(), // to ensure that escaped characters are not lost EscapedCharacterExtension.create(), SuperscriptExtension.create(), diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkFactory.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkFactory.java index c48378817..4a8c6c36d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkFactory.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkFactory.java @@ -1,103 +1,148 @@ -/* - * 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.datatype.markup.flexmark; - -import com.vladsch.flexmark.formatter.Formatter; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; -import com.vladsch.flexmark.parser.ListOptions; -import com.vladsch.flexmark.parser.Parser; -import com.vladsch.flexmark.util.data.DataHolder; - -import edu.umd.cs.findbugs.annotations.NonNull; - -@SuppressWarnings("PMD.DataClass") -public final class FlexmarkFactory { - @NonNull - private static final FlexmarkFactory SINGLETON = new FlexmarkFactory(); - @NonNull - private final Parser markdownParser; - @NonNull - private final HtmlRenderer htmlRenderer; - @NonNull - private final Formatter formatter; - @NonNull - private final FlexmarkHtmlConverter htmlConverter; - @NonNull - final ListOptions listOptions; - - @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") - @NonNull - public static synchronized FlexmarkFactory instance() { - return SINGLETON; - } - - @NonNull - public static FlexmarkFactory newInstance(@NonNull DataHolder config) { - return new FlexmarkFactory(config); - } - - private FlexmarkFactory() { - this(FlexmarkConfiguration.FLEXMARK_CONFIG); - } - - @SuppressWarnings("null") - private FlexmarkFactory(@NonNull DataHolder config) { - this.markdownParser = Parser.builder(config) - .customDelimiterProcessor(new FixedEmphasisDelimiterProcessor(Parser.STRONG_WRAPS_EMPHASIS.get(config))) - .build(); - this.htmlRenderer = HtmlRenderer.builder(config).build(); - this.formatter = Formatter.builder(config).build(); - this.htmlConverter = FlexmarkHtmlConverter.builder(config).build(); - this.listOptions = ListOptions.get(config); - } - - @NonNull - public ListOptions getListOptions() { - return listOptions; - } - - @NonNull - public Parser getMarkdownParser() { - return markdownParser; - } - - @NonNull - public HtmlRenderer getHtmlRenderer() { - return htmlRenderer; - } - - @NonNull - public Formatter getFormatter() { - return formatter; - } - - @NonNull - public FlexmarkHtmlConverter getFlexmarkHtmlConverter() { - return htmlConverter; - } -} +/* + * 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.datatype.markup.flexmark; + +import com.vladsch.flexmark.formatter.Formatter; +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; +import com.vladsch.flexmark.parser.ListOptions; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.data.DataHolder; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Provides factory methods for Flexmark processing to support HTML-to-markdown + * and markdown-to-HTML conversion. + */ +@SuppressWarnings("PMD.DataClass") +public final class FlexmarkFactory { + @NonNull + private static final FlexmarkFactory SINGLETON = new FlexmarkFactory(); + @NonNull + private final Parser markdownParser; + @NonNull + private final HtmlRenderer htmlRenderer; + @NonNull + private final Formatter formatter; + @NonNull + private final FlexmarkHtmlConverter htmlConverter; + @NonNull + final ListOptions listOptions; + + /** + * Get the static Flexmark factory instance. + * + * @return the instance + */ + @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") + @NonNull + public static synchronized FlexmarkFactory instance() { + return SINGLETON; + } + + /** + * Get a Flexmark factory instance that uses the provided Flexmark + * configuration. + * + * @param config + * the Flexmark configuration + * @return the instance + */ + @NonNull + public static FlexmarkFactory newInstance(@NonNull DataHolder config) { + return new FlexmarkFactory(config); + } + + private FlexmarkFactory() { + this(FlexmarkConfiguration.FLEXMARK_CONFIG); + } + + @SuppressWarnings("null") + private FlexmarkFactory(@NonNull DataHolder config) { + this.markdownParser = Parser.builder(config) + .customDelimiterProcessor(new FixedEmphasisDelimiterProcessor(Parser.STRONG_WRAPS_EMPHASIS.get(config))) + .build(); + this.htmlRenderer = HtmlRenderer.builder(config).build(); + this.formatter = Formatter.builder(config).build(); + this.htmlConverter = FlexmarkHtmlConverter.builder(config).build(); + this.listOptions = ListOptions.get(config); + } + + /** + * Get configured options for processing HTML and markdown lists. + * + * @return the options + */ + @NonNull + public ListOptions getListOptions() { + return listOptions; + } + + /** + * Get the Flexmark markdown parser, which can produce a markdown syntax tree. + * + * @return the parser + */ + @NonNull + public Parser getMarkdownParser() { + return markdownParser; + } + + /** + * Get the Flexmark HTML renderer, which can produce HTML from a markdown syntax + * tree. + * + * @return the parser + */ + @NonNull + public HtmlRenderer getHtmlRenderer() { + return htmlRenderer; + } + + /** + * Get the Flexmark formatter, which can produce markdown from a markdown syntax + * tree. + * + * @return the parser + */ + @NonNull + public Formatter getFormatter() { + return formatter; + } + + /** + * Get the Flexmark HTML converter, which can produce markdown from HTML + * content. + * + * @return the parser + */ + @NonNull + public FlexmarkHtmlConverter getFlexmarkHtmlConverter() { + return htmlConverter; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlCodeRenderExtension.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlCodeRenderExtension.java index e202be5fc..2b1521d9a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlCodeRenderExtension.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlCodeRenderExtension.java @@ -57,7 +57,12 @@ public class HtmlCodeRenderExtension implements HtmlRenderer.HtmlRendererExtension { private static final Pattern EOL_PATTERN = Pattern.compile("\r\n|\r|\n"); - public static HtmlCodeRenderExtension create() { + /** + * Construct a new extension instance. + * + * @return the instance + */ + public static HtmlCodeRenderExtension newInstance() { return new HtmlCodeRenderExtension(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlQuoteTagExtension.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlQuoteTagExtension.java index e5cc2434a..1d52640e7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlQuoteTagExtension.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlQuoteTagExtension.java @@ -61,7 +61,12 @@ public class HtmlQuoteTagExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension, FlexmarkHtmlConverter.HtmlConverterExtension { - public static HtmlQuoteTagExtension create() { + /** + * Construct a new extension instance. + * + * @return the instance + */ + public static HtmlQuoteTagExtension newInstance() { return new HtmlQuoteTagExtension(); } @@ -172,6 +177,13 @@ public HtmlNodeRenderer apply(DataHolder options) { public static class DoubleQuoteNode extends TypographicQuotes { + /** + * Construct a new double quote node. + * + * @param node + * the typographic information pertaining to a double quote + */ + @SuppressWarnings("PMD.ConstructorCallsOverridableMethod") public DoubleQuoteNode(TypographicQuotes node) { super(node.getOpeningMarker(), node.getText(), node.getClosingMarker()); setTypographicOpening(node.getTypographicOpening()); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupVisitor.java index d8a0a119b..8c81288e8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupVisitor.java @@ -33,5 +33,15 @@ @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_THROWABLE") public interface IMarkupVisitor { + /** + * A visitor callback used to visit a markdown syntax tree. + * + * @param document + * the markdown syntax tree + * @param writer + * a markup writer used to generate markup output + * @throws E + * the visitor exception Java type + */ void visitDocument(@NonNull Document document, @NonNull IMarkupWriter writer) throws E; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupWriter.java index b95169762..253e50176 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupWriter.java @@ -41,7 +41,6 @@ import com.vladsch.flexmark.ast.IndentedCodeBlock; import com.vladsch.flexmark.ast.Link; import com.vladsch.flexmark.ast.ListBlock; -import com.vladsch.flexmark.ast.ListItem; import com.vladsch.flexmark.ast.MailLink; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.ast.Text; @@ -58,8 +57,6 @@ import java.util.Map; -import javax.xml.namespace.QName; - import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -67,9 +64,18 @@ @SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "There is a need to support varying exceptions from multiple stream writers") public interface IMarkupWriter { // NOPMD - @NonNull - QName asQName(@NonNull String localName); - + /** + * Write an HTML element with the provided local name, with no attributes. + * + * @param localName + * the element name + * @param node + * the markup node related to the element + * @param childHandler + * a handler used to process child node content + * @throws E + * if an error occurred while writing the markup + */ default void writeElement( @NonNull String localName, @NonNull Node node, @@ -77,135 +83,336 @@ default void writeElement( writeElement(localName, node, CollectionUtil.emptyMap(), childHandler); } - default void writeElement( - @NonNull String localName, - @NonNull Node node, - @NonNull Map attributes, - @Nullable ChildHandler childHandler) throws E { - QName qname = asQName(localName); - writeElement(qname, node, attributes, childHandler); - } - + /** + * Write an HTML element with the provided local name. + * + * @param localName + * the element name + * @param node + * the markup node related to the element + * @param attributes + * attributes associated with the element to also write + * @param childHandler + * the handler used to process child node content or {@code null} + * @throws E + * if an error occurred while writing the markup + */ void writeElement( - @NonNull QName qname, + @NonNull String localName, @NonNull Node node, @NonNull Map attributes, @Nullable ChildHandler childHandler) throws E; - default void writeEmptyElement( - @NonNull String localName, - @NonNull Map attributes) throws E { - QName qname = asQName(localName); - writeEmptyElement(qname, attributes); - } - + /** + * Write an empty HTML element with the provided local name. + * + * @param localName + * the element name + * @param attributes + * attributes associated with the element to also write + * @throws E + * if an error occurred while writing the markup + */ void writeEmptyElement( - @NonNull QName qname, - @NonNull Map attributes) throws E; - - default void writeElementStart( - @NonNull QName qname) throws E { - writeElementStart(qname, CollectionUtil.emptyMap()); - } - - void writeElementStart( - @NonNull QName qname, + @NonNull String localName, @NonNull Map attributes) throws E; - void writeElementEnd(@NonNull QName qname) throws E; - + /** + * Write a text node. + * + * @param node + * the text node to write + * @throws E + * if an error occurred while writing + */ void writeText(@NonNull Text node) throws E; /** - * Handle a combination of {@link Text} and {@link EscapedCharacter} node + * Write a combination of {@link Text} and {@link EscapedCharacter} node * children. * * @param node * the text node to write * @throws E - * if an error occured while writing + * if an error occurred while writing */ void writeText(@NonNull TextBase node) throws E; + /** + * Write a text string. + * + * @param text + * the text to write + * @throws E + * if an error occurred while writing + */ void writeText(@NonNull CharSequence text) throws E; + /** + * Write an HTML entity. + * + * @param node + * the entity node + * @throws E + * if an error occurred while writing + */ void writeHtmlEntity(@NonNull HtmlEntity node) throws E; + /** + * Write an HTML entity for a typographic character. + * + * @param node + * the entity node + * @throws E + * if an error occurred while writing + */ void writeHtmlEntity(@NonNull TypographicSmarts node) throws E; + /** + * Write a paragraph. + * + * @param node + * the paragraph node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeParagraph( @NonNull Paragraph node, @NonNull ChildHandler childHandler) throws E; + /** + * Write a link. + * + * @param node + * the link node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeLink( @NonNull Link node, @NonNull ChildHandler childHandler) throws E; + /** + * Write an email link. + * + * @param node + * the link node + * @throws E + * if an error occurred while writing + */ void writeLink(@NonNull MailLink node) throws E; + /** + * Write a link that was auto-detected. + * + * @param node + * the link node + * @throws E + * if an error occurred while writing + */ void writeLink(@NonNull AutoLink node) throws E; + /** + * Write a typographic quote(s). + * + * @param node + * the quote node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeTypographicQuotes( @NonNull TypographicQuotes node, @NonNull ChildHandler childHandler) throws E; + /** + * Write embedded HTML content. + * + * @param node + * the HTML content + * @throws E + * if an error occurred while writing + */ void writeInlineHtml(@NonNull HtmlInline node) throws E; + /** + * Write HTML block content. + * + * @param node + * the HTML block + * @throws E + * if an error occurred while writing + */ void writeBlockHtml(@NonNull HtmlBlock node) throws E; + /** + * Write a table. + * + * @param node + * the table node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeTable( @NonNull TableBlock node, - @NonNull ChildHandler cellChilddHandler) throws E; + @NonNull ChildHandler childHandler) throws E; + /** + * Write an image. + * + * @param node + * the image node + * @throws E + * if an error occurred while writing + */ void writeImage(@NonNull Image node) throws E; + /** + * Write a Metaschema markup insertion point. + * + * @param node + * the insert node + * @throws E + * if an error occurred while writing + */ void writeInsertAnchor(@NonNull InsertAnchorNode node) throws E; + /** + * Write a heading. + * + * @param node + * the heading node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeHeading( @NonNull Heading node, @NonNull ChildHandler childHandler) throws E; + /** + * Write an inline code block. + * + * @param node + * the code node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeCode( @NonNull Code node, @NonNull ChildHandler childHandler) throws E; + /** + * Write an indented code block. + * + * @param node + * the code node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeCodeBlock( @NonNull IndentedCodeBlock node, @NonNull ChildHandler childHandler) throws E; + /** + * Write a fenced code block. + * + * @param node + * the code node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeCodeBlock( @NonNull FencedCodeBlock node, @NonNull ChildHandler childHandler) throws E; + /** + * Write a code block. + * + * @param node + * the code node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeCodeBlock( @NonNull CodeBlock node, @NonNull ChildHandler childHandler) throws E; + /** + * Write a block quotation. + * + * @param node + * the quotation node + * @param childHandler + * the handler used to process child node content + * @throws E + * if an error occurred while writing + */ void writeBlockQuote( @NonNull BlockQuote node, @NonNull ChildHandler childHandler) throws E; - default void writeList( - @NonNull String localName, - @NonNull ListBlock node, - @NonNull ChildHandler listItemHandler) throws E { - QName qname = asQName(localName); - writeList(qname, node, listItemHandler); - } - + /** + * Write a list. + * + * @param localName + * the HTML element name to use + * @param node + * the list node + * @param handler + * the list item handler + * @throws E + * if an error occurred while writing + */ void writeList( - @NonNull QName qname, + @NonNull String localName, @NonNull ListBlock node, - @NonNull ChildHandler listItemHandler) throws E; - - void writeListItem( - @NonNull ListItem node, - @NonNull ChildHandler listItemHandler) throws E; + @NonNull ChildHandler handler) throws E; + /** + * Write a line break. + * + * @param node + * the break node + * @throws E + * if an error occurred while writing + */ void writeBreak(@NonNull HardLineBreak node) throws E; + /** + * Write a line break. + * + * @param node + * the break node + * @throws E + * if an error occurred while writing + */ void writeBreak(@NonNull ThematicBreak node) throws E; + /** + * Write a comment. + * + * @param node + * the comment node + * @throws E + * if an error occurred while writing + */ void writeComment(@NonNull HtmlCommentBlock node) throws E; /** @@ -217,7 +424,17 @@ void writeListItem( * the type of exception that can be thrown when a writing error occurs */ @FunctionalInterface - interface ChildHandler { // NOPMD + interface ChildHandler { + /** + * A callback used to process a given node. + * + * @param node + * the node to process + * @param writer + * used to write if an error occurred while writing + * @throws E + * if an error occurred while writing + */ void accept(@NonNull Node node, @NonNull IMarkupWriter writer) throws E; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertAnchorExtension.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertAnchorExtension.java index 8300faa5a..902b35dd9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertAnchorExtension.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertAnchorExtension.java @@ -1,348 +1,433 @@ -/* - * 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.datatype.markup.flexmark; // NOPMD AST processor - -import com.vladsch.flexmark.formatter.Formatter; -import com.vladsch.flexmark.formatter.MarkdownWriter; -import com.vladsch.flexmark.formatter.NodeFormatter; -import com.vladsch.flexmark.formatter.NodeFormatterContext; -import com.vladsch.flexmark.formatter.NodeFormatterFactory; -import com.vladsch.flexmark.formatter.NodeFormattingHandler; -import com.vladsch.flexmark.html.HtmlRenderer; -import com.vladsch.flexmark.html.HtmlWriter; -import com.vladsch.flexmark.html.renderer.NodeRenderer; -import com.vladsch.flexmark.html.renderer.NodeRendererContext; -import com.vladsch.flexmark.html.renderer.NodeRendererFactory; -import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; -import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; -import com.vladsch.flexmark.html2md.converter.HtmlMarkdownWriter; -import com.vladsch.flexmark.html2md.converter.HtmlNodeConverterContext; -import com.vladsch.flexmark.html2md.converter.HtmlNodeRenderer; -import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererFactory; -import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererHandler; -import com.vladsch.flexmark.parser.InlineParser; -import com.vladsch.flexmark.parser.InlineParserExtension; -import com.vladsch.flexmark.parser.InlineParserExtensionFactory; -import com.vladsch.flexmark.parser.LightInlineParser; -import com.vladsch.flexmark.parser.Parser; -import com.vladsch.flexmark.util.ast.Node; -import com.vladsch.flexmark.util.data.DataHolder; -import com.vladsch.flexmark.util.data.DataKey; -import com.vladsch.flexmark.util.data.MutableDataHolder; -import com.vladsch.flexmark.util.misc.Extension; -import com.vladsch.flexmark.util.sequence.BasedSequence; -import com.vladsch.flexmark.util.sequence.CharSubSequence; - -import org.jsoup.nodes.Element; - -import java.util.Collections; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class InsertAnchorExtension - implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension, - Formatter.FormatterExtension, FlexmarkHtmlConverter.HtmlConverterExtension { - public static final DataKey ENABLE_INLINE_INSERT_ANCHORS - = new DataKey<>("ENABLE_INLINE_INSERT_ANCHORS", true); - public static final DataKey ENABLE_RENDERING = new DataKey<>("ENABLE_RENDERING", true); - - public static Extension create() { - return new InsertAnchorExtension(); - } - - @Override - public void parserOptions(MutableDataHolder options) { - // do nothing - } - - @Override - public void rendererOptions(MutableDataHolder options) { - // do nothing - } - - @Override - public void extend(HtmlRenderer.Builder rendererBuilder, String rendererType) { - rendererBuilder.nodeRendererFactory(new InsertAnchorNodeRenderer.Factory()); - } - - @Override - public void extend(Parser.Builder parserBuilder) { - if (ENABLE_INLINE_INSERT_ANCHORS.get(parserBuilder)) { - parserBuilder.customInlineParserExtensionFactory(new InsertAnchorInlineParser.Factory()); - } - } - - @Override - public void extend(Formatter.Builder builder) { - builder.nodeFormatterFactory(new InsertAnchorFormatter.Factory()); - } - - @Override - public void extend(FlexmarkHtmlConverter.Builder builder) { - builder.htmlNodeRendererFactory(new InsertAnchorHtmlNodeRenderer.Factory()); - } - - private static class InsertAnchorOptions { - public final boolean enableInlineInsertAnchors; - public final boolean enableRendering; - - public InsertAnchorOptions(DataHolder options) { - enableInlineInsertAnchors = ENABLE_INLINE_INSERT_ANCHORS.get(options); - enableRendering = ENABLE_RENDERING.get(options); - } - } - - private static class InsertAnchorNodeRenderer implements NodeRenderer { - private final InsertAnchorOptions options; - - public InsertAnchorNodeRenderer(DataHolder options) { - this.options = new InsertAnchorOptions(options); - } - - @Override - public Set> getNodeRenderingHandlers() { - return Collections.singleton(new NodeRenderingHandler<>(InsertAnchorNode.class, this::render)); - } - - @SuppressWarnings("unused") - protected void render(InsertAnchorNode node, NodeRendererContext context, HtmlWriter html) { - if (options.enableRendering) { - html.attr("type", node.getType()).attr("id-ref", node.getIdReference()).withAttr().tagVoid("insert"); - } - } - - // @Override - // public Set> getNodeRenderingHandlers() { - // HashSet> set = new - // HashSet>(); - // set.add(new NodeRenderingHandler(Macro.class, new - // CustomNodeRenderer() { - // @Override - // public void render(Macro node, NodeRendererContext context, HtmlWriter html) - // { - // MacroNodeRenderer.this.render(node, context, html); } - // })); - public static class Factory implements NodeRendererFactory { - - @Override - public NodeRenderer apply(DataHolder options) { - return new InsertAnchorNodeRenderer(options); - } - - } - - } - - private static class InsertAnchorInlineParser implements InlineParserExtension { - private static final Pattern PATTERN = Pattern.compile("\\{\\{\\s*insert:\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\}\\}"); - - public InsertAnchorInlineParser(@SuppressWarnings("unused") LightInlineParser inlineParser) { - // do nothing - } - - @Override - public void finalizeDocument(InlineParser inlineParser) { - // do nothing - } - - @Override - public void finalizeBlock(InlineParser inlineParser) { - // do nothing - } - - @Override - public boolean parse(LightInlineParser inlineParser) { - if (inlineParser.peek() == '{') { - BasedSequence input = inlineParser.getInput(); - Matcher matcher = inlineParser.matcher(PATTERN); - if (matcher != null) { - BasedSequence type = input.subSequence(matcher.start(1), matcher.end(1)); - BasedSequence idReference = input.subSequence(matcher.start(2), matcher.end(2)); - assert type != null; - assert idReference != null; - inlineParser.appendNode(new InsertAnchorNode(type, idReference)); - return true; // NOPMD - readability - } - } - return false; - } - - public static class Factory implements InlineParserExtensionFactory { - @Override - public Set> getAfterDependents() { - return Collections.emptySet(); - } - - @Override - public CharSequence getCharacters() { - return "{"; - } - - @Override - public Set> getBeforeDependents() { - return Collections.emptySet(); - } - - @Override - public InlineParserExtension apply(LightInlineParser lightInlineParser) { - return new InsertAnchorInlineParser(lightInlineParser); - } - - @Override - public boolean affectsGlobalScope() { - return false; - } - } - } - - private static class InsertAnchorFormatter implements NodeFormatter { - private final InsertAnchorOptions options; - - public InsertAnchorFormatter(DataHolder options) { - this.options = new InsertAnchorOptions(options); - } - - @Override - public Set> getNodeFormattingHandlers() { - return options.enableInlineInsertAnchors - ? Collections.singleton(new NodeFormattingHandler<>(InsertAnchorNode.class, this::render)) - : Collections.emptySet(); - } - - @SuppressWarnings("unused") - protected void render(InsertAnchorNode node, NodeFormatterContext context, MarkdownWriter markdown) { - if (options.enableRendering) { - markdown.append("{{ insert: "); - markdown.append(node.getType()); - markdown.append(", "); - markdown.append(node.getIdReference()); - markdown.append(" }}"); - } - } - - @Override - public Set> getNodeClasses() { - return Collections.singleton(InsertAnchorNode.class); - } - - public static class Factory implements NodeFormatterFactory { - - @Override - public NodeFormatter create(DataHolder options) { - return new InsertAnchorFormatter(options); - } - - } - } - - private static class InsertAnchorHtmlNodeRenderer implements HtmlNodeRenderer { - private final InsertAnchorOptions options; - - public InsertAnchorHtmlNodeRenderer(DataHolder options) { - this.options = new InsertAnchorOptions(options); - } - - @Override - public Set> getHtmlNodeRendererHandlers() { - return options.enableInlineInsertAnchors - ? Collections.singleton(new HtmlNodeRendererHandler<>("insert", Element.class, this::processInsert)) - : Collections.emptySet(); - } - - private void processInsert( // NOPMD used as lambda - Element node, - @SuppressWarnings("unused") HtmlNodeConverterContext context, - HtmlMarkdownWriter out) { - - String type = node.attr("type"); - String idRef = node.attr("id-ref"); - - out.append("{{ insert: "); - out.append(type); - out.append(", "); - out.append(idRef); - out.append(" }}"); - } - - public static class Factory implements HtmlNodeRendererFactory { - - @Override - public HtmlNodeRenderer apply(DataHolder options) { - return new InsertAnchorHtmlNodeRenderer(options); - } - } - } - - public static class InsertAnchorNode - extends Node { - - @NonNull - private BasedSequence type; - @NonNull - private BasedSequence idReference; - - @SuppressWarnings("null") - public InsertAnchorNode(@NonNull String type, @NonNull String idReference) { - this(CharSubSequence.of(type), CharSubSequence.of(idReference)); - } - - public InsertAnchorNode(@NonNull BasedSequence type, @NonNull BasedSequence idReference) { - this.type = type; - this.idReference = idReference; - } - - @NonNull - public BasedSequence getType() { - return type; - } - - @NonNull - public BasedSequence getIdReference() { - return idReference; - } - - public void setIdReference(@NonNull BasedSequence value) { - this.idReference = value; - } - - @Override - @NonNull - public BasedSequence[] getSegments() { - @NonNull BasedSequence[] retval = { getType(), getIdReference() }; - return retval; - } - - @Override - public void getAstExtra(StringBuilder out) { - segmentSpanChars(out, getType(), "type"); - segmentSpanChars(out, getIdReference(), "id-ref"); - } - } -} +/* + * 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.datatype.markup.flexmark; // NOPMD AST processor + +import com.vladsch.flexmark.formatter.Formatter; +import com.vladsch.flexmark.formatter.MarkdownWriter; +import com.vladsch.flexmark.formatter.NodeFormatter; +import com.vladsch.flexmark.formatter.NodeFormatterContext; +import com.vladsch.flexmark.formatter.NodeFormatterFactory; +import com.vladsch.flexmark.formatter.NodeFormattingHandler; +import com.vladsch.flexmark.html.HtmlRenderer; +import com.vladsch.flexmark.html.HtmlWriter; +import com.vladsch.flexmark.html.renderer.NodeRenderer; +import com.vladsch.flexmark.html.renderer.NodeRendererContext; +import com.vladsch.flexmark.html.renderer.NodeRendererFactory; +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler; +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter; +import com.vladsch.flexmark.html2md.converter.HtmlMarkdownWriter; +import com.vladsch.flexmark.html2md.converter.HtmlNodeConverterContext; +import com.vladsch.flexmark.html2md.converter.HtmlNodeRenderer; +import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererFactory; +import com.vladsch.flexmark.html2md.converter.HtmlNodeRendererHandler; +import com.vladsch.flexmark.parser.InlineParser; +import com.vladsch.flexmark.parser.InlineParserExtension; +import com.vladsch.flexmark.parser.InlineParserExtensionFactory; +import com.vladsch.flexmark.parser.LightInlineParser; +import com.vladsch.flexmark.parser.Parser; +import com.vladsch.flexmark.util.ast.Node; +import com.vladsch.flexmark.util.ast.NodeVisitorBase; +import com.vladsch.flexmark.util.data.DataHolder; +import com.vladsch.flexmark.util.data.DataKey; +import com.vladsch.flexmark.util.data.MutableDataHolder; +import com.vladsch.flexmark.util.sequence.BasedSequence; +import com.vladsch.flexmark.util.sequence.CharSubSequence; + +import gov.nist.secauto.metaschema.core.datatype.markup.IMarkupString; + +import org.jsoup.nodes.Element; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class InsertAnchorExtension + implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension, + Formatter.FormatterExtension, FlexmarkHtmlConverter.HtmlConverterExtension { + public static final DataKey ENABLE_INLINE_INSERT_ANCHORS + = new DataKey<>("ENABLE_INLINE_INSERT_ANCHORS", true); + public static final DataKey ENABLE_RENDERING = new DataKey<>("ENABLE_RENDERING", true); + + /** + * Construct a new extension instance. + * + * @return the instance + */ + public static InsertAnchorExtension newInstance() { + return new InsertAnchorExtension(); + } + + @Override + public void parserOptions(MutableDataHolder options) { + // do nothing + } + + @Override + public void rendererOptions(MutableDataHolder options) { + // do nothing + } + + @Override + public void extend(HtmlRenderer.Builder rendererBuilder, String rendererType) { + rendererBuilder.nodeRendererFactory(new InsertAnchorNodeRenderer.Factory()); + } + + @Override + public void extend(Parser.Builder parserBuilder) { + if (ENABLE_INLINE_INSERT_ANCHORS.get(parserBuilder)) { + parserBuilder.customInlineParserExtensionFactory(new InsertAnchorInlineParser.Factory()); + } + } + + @Override + public void extend(Formatter.Builder builder) { + builder.nodeFormatterFactory(new InsertAnchorFormatter.Factory()); + } + + @Override + public void extend(FlexmarkHtmlConverter.Builder builder) { + builder.htmlNodeRendererFactory(new InsertAnchorHtmlNodeRenderer.Factory()); + } + + private static class InsertAnchorOptions { + public final boolean enableInlineInsertAnchors; + public final boolean enableRendering; + + public InsertAnchorOptions(DataHolder options) { + enableInlineInsertAnchors = ENABLE_INLINE_INSERT_ANCHORS.get(options); + enableRendering = ENABLE_RENDERING.get(options); + } + } + + private static class InsertAnchorNodeRenderer implements NodeRenderer { + private final InsertAnchorOptions options; + + public InsertAnchorNodeRenderer(DataHolder options) { + this.options = new InsertAnchorOptions(options); + } + + @Override + public Set> getNodeRenderingHandlers() { + return Collections.singleton(new NodeRenderingHandler<>(InsertAnchorNode.class, this::render)); + } + + @SuppressWarnings("unused") + protected void render(InsertAnchorNode node, NodeRendererContext context, HtmlWriter html) { + if (options.enableRendering) { + html.attr("type", node.getType()).attr("id-ref", node.getIdReference()).withAttr().tagVoid("insert"); + } + } + + public static class Factory implements NodeRendererFactory { + + @Override + public NodeRenderer apply(DataHolder options) { + return new InsertAnchorNodeRenderer(options); + } + } + } + + private static class InsertAnchorInlineParser implements InlineParserExtension { + private static final Pattern PATTERN = Pattern.compile("\\{\\{\\s*insert:\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\}\\}"); + + public InsertAnchorInlineParser(@SuppressWarnings("unused") LightInlineParser inlineParser) { + // do nothing + } + + @Override + public void finalizeDocument(InlineParser inlineParser) { + // do nothing + } + + @Override + public void finalizeBlock(InlineParser inlineParser) { + // do nothing + } + + @Override + public boolean parse(LightInlineParser inlineParser) { + if (inlineParser.peek() == '{') { + BasedSequence input = inlineParser.getInput(); + Matcher matcher = inlineParser.matcher(PATTERN); + if (matcher != null) { + BasedSequence type = input.subSequence(matcher.start(1), matcher.end(1)); + BasedSequence idReference = input.subSequence(matcher.start(2), matcher.end(2)); + assert type != null; + assert idReference != null; + inlineParser.appendNode(new InsertAnchorNode(type, idReference)); + return true; // NOPMD - readability + } + } + return false; + } + + public static class Factory implements InlineParserExtensionFactory { + @Override + public Set> getAfterDependents() { + return Collections.emptySet(); + } + + @Override + public CharSequence getCharacters() { + return "{"; + } + + @Override + public Set> getBeforeDependents() { + return Collections.emptySet(); + } + + @Override + public InlineParserExtension apply(LightInlineParser lightInlineParser) { + return new InsertAnchorInlineParser(lightInlineParser); + } + + @Override + public boolean affectsGlobalScope() { + return false; + } + } + } + + private static class InsertAnchorFormatter implements NodeFormatter { + private final InsertAnchorOptions options; + + public InsertAnchorFormatter(DataHolder options) { + this.options = new InsertAnchorOptions(options); + } + + @Override + public Set> getNodeFormattingHandlers() { + return options.enableInlineInsertAnchors + ? Collections.singleton(new NodeFormattingHandler<>(InsertAnchorNode.class, this::render)) + : Collections.emptySet(); + } + + @SuppressWarnings("unused") + protected void render(InsertAnchorNode node, NodeFormatterContext context, MarkdownWriter markdown) { + if (options.enableRendering) { + markdown.append("{{ insert: "); + markdown.append(node.getType()); + markdown.append(", "); + markdown.append(node.getIdReference()); + markdown.append(" }}"); + } + } + + @Override + public Set> getNodeClasses() { + return Collections.singleton(InsertAnchorNode.class); + } + + public static class Factory implements NodeFormatterFactory { + + @Override + public NodeFormatter create(DataHolder options) { + return new InsertAnchorFormatter(options); + } + + } + } + + private static class InsertAnchorHtmlNodeRenderer implements HtmlNodeRenderer { + private final InsertAnchorOptions options; + + public InsertAnchorHtmlNodeRenderer(DataHolder options) { + this.options = new InsertAnchorOptions(options); + } + + @Override + public Set> getHtmlNodeRendererHandlers() { + return options.enableInlineInsertAnchors + ? Collections.singleton(new HtmlNodeRendererHandler<>("insert", Element.class, this::processInsert)) + : Collections.emptySet(); + } + + private void processInsert( // NOPMD used as lambda + Element node, + @SuppressWarnings("unused") HtmlNodeConverterContext context, + HtmlMarkdownWriter out) { + + String type = node.attr("type"); + String idRef = node.attr("id-ref"); + + out.append("{{ insert: "); + out.append(type); + out.append(", "); + out.append(idRef); + out.append(" }}"); + } + + public static class Factory implements HtmlNodeRendererFactory { + + @Override + public HtmlNodeRenderer apply(DataHolder options) { + return new InsertAnchorHtmlNodeRenderer(options); + } + } + } + + public static class InsertAnchorNode + extends Node { + + @NonNull + private final BasedSequence type; + @NonNull + private BasedSequence idReference; + + /** + * Construct a new Metaschema insert node. + * + * @param type + * the type of insertion + * @param idReference + * the identifier of the given type to use to determine what to insert + */ + @SuppressWarnings("null") + public InsertAnchorNode(@NonNull String type, @NonNull String idReference) { + this(CharSubSequence.of(type), CharSubSequence.of(idReference)); + } + + /** + * Construct a new Metaschema insert node. + * + * @param type + * the type of insertion + * @param idReference + * the identifier of the given type to use to determine what to insert + */ + protected InsertAnchorNode(@NonNull BasedSequence type, @NonNull BasedSequence idReference) { + this.type = type; + this.idReference = idReference; + } + + /** + * Get the type of insertion. + * + * @return the type of insertion + */ + @NonNull + public BasedSequence getType() { + return type; + } + + /** + * Get the identifier of the given type to use to determine what to insert. + * + * @return the identifier + */ + @NonNull + public BasedSequence getIdReference() { + return idReference; + } + + /** + * Set the identifier of the given type to use to determine what to insert. + * + * @param idReference + * the identifier + */ + public void setIdReference(@NonNull BasedSequence idReference) { + this.idReference = idReference; + } + + @Override + @NonNull + public BasedSequence[] getSegments() { + @NonNull BasedSequence[] retval = { getType(), getIdReference() }; + return retval; + } + + @Override + public void getAstExtra(StringBuilder out) { + segmentSpanChars(out, getType(), "type"); + segmentSpanChars(out, getIdReference(), "id-ref"); + } + } + + /** + * Used to collect insert nodes. + */ + public static class InsertVisitor + extends NodeVisitorBase { + @NonNull + private final List inserts = new LinkedList<>(); + @NonNull + private final Predicate filter; + + /** + * Construct a new visitor that will use the provided filter to visit matching + * insert nodes. + * + * @param filter + * the match criteria to use to identify matching insert nodes + */ + public InsertVisitor(@NonNull Predicate filter) { + this.filter = filter; + } + + /** + * Process markup to identify insert nodes. + * + * @param markup + * the markup to process + * @return this visitor + */ + public InsertVisitor processNode(@NonNull IMarkupString markup) { + visit(markup.getDocument()); + return this; + } + + @Override + protected void visit(Node node) { + if (node instanceof InsertAnchorNode) { + InsertAnchorNode insert = (InsertAnchorNode) node; + if (filter.test(insert)) { + inserts.add((InsertAnchorNode) node); + } + } else { + visitChildren(node); + } + } + + /** + * Get the collected insert nodes. + * + * @return the nodes + */ + @NonNull + public List getInserts() { + return inserts; + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupVisitor.java index 42d44158a..c9fff8b52 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupVisitor.java @@ -69,7 +69,8 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** - * + * Supports the production of HTML content based on a markup syntax tree. + *

* This implementation is stateless. * * @param @@ -81,10 +82,23 @@ public class MarkupVisitor implements IMarkupVisitor { private final boolean handleBlockElements; + /** + * Construct a new visitor. + * + * @param handleBlockElements + * {@code true} process block content or {@code false} process the tree + * as a single line of markup + */ public MarkupVisitor(boolean handleBlockElements) { this.handleBlockElements = handleBlockElements; } + /** + * Determine block processing state. + * + * @return {@code true} process block content or {@code false} process the tree + * as a single line of markup + */ protected boolean isHandleBlockElements() { return handleBlockElements; } @@ -94,13 +108,33 @@ public void visitDocument(Document document, IMarkupWriter writer) throws visitChildren(document, writer); } - protected void visitChildren(@NonNull Node parentNode, @NonNull IMarkupWriter writer) throws E { - for (Node node : parentNode.getChildren()) { + /** + * Process child nodes of the provided parent. + * + * @param parent + * the node whose children are to be processed + * @param writer + * the markup writer used to write the HTML + * @throws E + * if an error occurred while writing + */ + protected void visitChildren(@NonNull Node parent, @NonNull IMarkupWriter writer) throws E { + for (Node node : parent.getChildren()) { assert node != null; visit(node, writer); } } + /** + * Process a node. + * + * @param node + * the node to process + * @param writer + * the markup writer used to write the HTML + * @throws E + * if an error occurred while writing + */ protected void visit(@NonNull Node node, @NonNull IMarkupWriter writer) throws E { boolean handled = processInlineElements(node, writer); if (!handled && node instanceof Block) { @@ -118,7 +152,19 @@ protected void visit(@NonNull Node node, @NonNull IMarkupWriter writer) th } } - protected boolean processInlineElements( // NOPMD dispatch method + /** + * Process a node that represents an inline element. + * + * @param node + * the node to process + * @param writer + * the markup writer used to write the HTML + * @return {@code true} if the node was processed or {@code false} otherwise + * @throws E + * if an error occurred while writing + */ + @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.CognitiveComplexity", "PMD.NcssCount" }) + protected boolean processInlineElements( @NonNull Node node, @NonNull IMarkupWriter writer) throws E { // NOPMD - acceptable boolean retval = true; @@ -172,7 +218,19 @@ protected boolean processInlineElements( // NOPMD dispatch method return retval; } - protected boolean processBlockElements( // NOPMD dispatch method + /** + * Process a node that represents a block element. + * + * @param node + * the node to process + * @param writer + * the markup writer used to write the HTML + * @return {@code true} if the node was processed or {@code false} otherwise + * @throws E + * if an error occurred while writing + */ + @SuppressWarnings("PMD.CyclomaticComplexity") + protected boolean processBlockElements( @NonNull Node node, @NonNull IMarkupWriter writer) throws E { boolean retval = true; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlEventWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlEventWriter.java index 16806f8e2..ccfe2e099 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlEventWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlEventWriter.java @@ -28,6 +28,7 @@ import com.vladsch.flexmark.parser.ListOptions; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.AbstractMarkupWriter; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -53,6 +54,18 @@ public class MarkupXmlEventWriter @NonNull protected final XMLEventFactory2 eventFactory; + /** + * Construct a new event writer. + * + * @param namespace + * the XML namespace to use for XMHTML content + * @param listOptions + * list production options + * @param writer + * the XML event stream to write to + * @param eventFactory + * the XML event factory used to generate XML events + */ public MarkupXmlEventWriter( @NonNull String namespace, @NonNull ListOptions listOptions, @@ -62,11 +75,23 @@ public MarkupXmlEventWriter( this.eventFactory = Objects.requireNonNull(eventFactory, "eventFactory"); } + /** + * Get the XML event factory used to generate XML events. + * + * @return the XML event factory + */ @NonNull protected XMLEventFactory2 getEventFactory() { return eventFactory; } + /** + * Get XML events for the provided attributes. + * + * @param attributes + * the mapping of attribute name to value + * @return the list of attribute events + */ @NonNull protected List handleAttributes(@NonNull Map attributes) { List attrs; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlStreamWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlStreamWriter.java index 3f79e7f4f..6c9d526cc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlStreamWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlStreamWriter.java @@ -28,6 +28,8 @@ import com.vladsch.flexmark.parser.ListOptions; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.AbstractMarkupWriter; + import java.util.Map; import javax.xml.namespace.QName; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/SuppressPTagExtension.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/SuppressPTagExtension.java index 5f3f9822e..3d947f34e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/SuppressPTagExtension.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/SuppressPTagExtension.java @@ -45,7 +45,12 @@ public class SuppressPTagExtension implements HtmlRenderer.HtmlRendererExtension { - public static SuppressPTagExtension create() { + /** + * Construct a new extension instance. + * + * @return the instance + */ + public static SuppressPTagExtension newInstance() { return new SuppressPTagExtension(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/XmlMarkupParser.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/XmlMarkupParser.java index cfe2b8959..065daca48 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/XmlMarkupParser.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/XmlMarkupParser.java @@ -1,247 +1,280 @@ -/* - * 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.datatype.markup.flexmark; - -import com.vladsch.flexmark.util.sequence.Escaping; - -import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; -import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; -import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; -import gov.nist.secauto.metaschema.core.util.CollectionUtil; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.codehaus.stax2.XMLEventReader2; - -import java.util.Set; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.Characters; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public final class XmlMarkupParser { - private static final Logger LOGGER = LogManager.getLogger(XmlMarkupParser.class); - - @NonNull - public static final Set BLOCK_ELEMENTS = ObjectUtils.notNull( - Set.of( - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "ul", - "ol", - "pre", - "hr", - "blockquote", - "p", - "table", - "img")); - - @NonNull - private static final XmlMarkupParser SINGLETON = new XmlMarkupParser(); - - @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") - @NonNull - public static synchronized XmlMarkupParser instance() { - return SINGLETON; - } - - private XmlMarkupParser() { - // disable construction - } - - public MarkupLine parseMarkupline(XMLEventReader2 reader) throws XMLStreamException { // NOPMD - acceptable - StringBuilder buffer = new StringBuilder(); - parseContents(reader, null, buffer); - String html = buffer.toString().trim(); - return html.isEmpty() ? null : MarkupLine.fromHtml(html); - } - - public MarkupMultiline parseMarkupMultiline(XMLEventReader2 reader) throws XMLStreamException { - StringBuilder buffer = new StringBuilder(); - parseToString(reader, buffer); - String html = buffer.toString().trim(); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("XML->HTML: {}", html); - } - return html.isEmpty() ? null : MarkupMultiline.fromHtml(html); - } - - private void parseToString(XMLEventReader2 reader, StringBuilder buffer) // NOPMD - acceptable - throws XMLStreamException { - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseToString(enter): {}", - // XmlEventUtil.toString(reader.peek())); - // } - - outer: while (reader.hasNextEvent() && !reader.peek().isEndElement()) { - // skip whitespace before the next block element - XMLEvent nextEvent = XmlEventUtil.skipWhitespace(reader); - - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseToString: {}", XmlEventUtil.toString(nextEvent)); - // } - - if (nextEvent.isStartElement()) { - StartElement start = nextEvent.asStartElement(); - QName name = start.getName(); - - // Note: the next element is not consumed. The called method is expected to - // consume it - if (BLOCK_ELEMENTS.contains(name.getLocalPart())) { - parseStartElement(reader, start, buffer); - - // the next event should be the event after the start's END_ELEMENT - // assert XmlEventUtil.isNextEventEndElement(reader, name) : - // XmlEventUtil.toString(reader.peek()); - } else { - // throw new IllegalStateException(); - // stop parsing on first unrecognized event - break outer; - } - } - // reader.nextEvent(); - - // skip whitespace before the next block element - XmlEventUtil.skipWhitespace(reader); - } - - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseToString(exit): {}", reader.peek() != null ? - // XmlEventUtil.toString(reader.peek()) : ""); - // } - } - - private void parseStartElement(XMLEventReader2 reader, StartElement start, StringBuilder buffer) - throws XMLStreamException { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("parseStartElement(enter): {}", XmlEventUtil.toString(start)); - } - - // consume the start event - reader.nextEvent(); - - QName name = start.getName(); - buffer.append('<') - .append(name.getLocalPart()); - for (Attribute attribute : CollectionUtil.toIterable( - ObjectUtils.notNull(start.getAttributes()))) { - buffer - .append(' ') - .append(attribute.getName().getLocalPart()) - .append("=\"") - .append(attribute.getValue()) - .append('"'); - } - - XMLEvent next = reader.peek(); - if (next != null && next.isEndElement()) { - buffer.append("/>"); - // consume end element event - reader.nextEvent(); - } else { - buffer.append('>'); - - // parse until the start's END_ELEMENT is reached - parseContents(reader, start, buffer); - - buffer - .append("'); - - // the next event should be the start's END_ELEMENT - XmlEventUtil.assertNext(reader, XMLStreamConstants.END_ELEMENT, name); - - // consume the start's END_ELEMENT - reader.nextEvent(); - } - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("parseStartElement(exit): {}", reader.peek() != null ? XmlEventUtil.toString(reader.peek()) : ""); - } - } - - private void parseContents(XMLEventReader2 reader, StartElement start, StringBuilder buffer) - throws XMLStreamException { - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseContents(enter): {}", - // XmlEventUtil.toString(reader.peek())); - // } - - XMLEvent event; - while (reader.hasNextEvent() && !(event = reader.peek()).isEndElement()) { - // // skip whitespace before the next list item - // event = XmlEventUtil.skipWhitespace(reader); - - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseContents(before): {}", XmlEventUtil.toString(event)); - // } - - if (event.isStartElement()) { - StartElement nextStart = event.asStartElement(); - // QName nextName = nextStart.getName(); - parseStartElement(reader, nextStart, buffer); - - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseContents(after): {}", - // XmlEventUtil.toString(reader.peek())); - // } - - // assert XmlEventUtil.isNextEventEndElement(reader, nextName) : - // XmlEventUtil.toString(reader.peek()); - - // reader.nextEvent(); - } else if (event.isCharacters()) { - Characters characters = event.asCharacters(); - buffer.append(Escaping.escapeHtml(characters.getData(), true)); - reader.nextEvent(); - } - } - - assert start == null - || XmlEventUtil.isEventEndElement(reader.peek(), ObjectUtils.notNull(start.getName())) : XmlEventUtil - .generateExpectedMessage(reader.peek(), XMLStreamConstants.END_ELEMENT, start.getName()); - - // if (LOGGER.isDebugEnabled()) { - // LOGGER.debug("parseContents(exit): {}", reader.peek() != null ? - // XmlEventUtil.toString(reader.peek()) : ""); - // } - } - -} +/* + * 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.datatype.markup.flexmark; + +import com.vladsch.flexmark.util.sequence.Escaping; + +import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; +import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.codehaus.stax2.XMLEventReader2; + +import java.util.Set; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.Characters; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class XmlMarkupParser { + private static final Logger LOGGER = LogManager.getLogger(XmlMarkupParser.class); + + @NonNull + public static final Set BLOCK_ELEMENTS = ObjectUtils.notNull( + Set.of( + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "ul", + "ol", + "pre", + "hr", + "blockquote", + "p", + "table", + "img")); + + @NonNull + private static final XmlMarkupParser SINGLETON = new XmlMarkupParser(); + + /** + * Get the singleton markup parser instance. + * + * @return the instance + */ + @SuppressWarnings("PMD.AvoidSynchronizedAtMethodLevel") + @NonNull + public static synchronized XmlMarkupParser instance() { + return SINGLETON; + } + + private XmlMarkupParser() { + // disable construction + } + + /** + * Parse a single line of markup from XHTML. + * + * @param reader + * the XML event stream reader + * @return the markup string + * @throws XMLStreamException + * if an error occurred while parsing + */ + public MarkupLine parseMarkupline(XMLEventReader2 reader) throws XMLStreamException { // NOPMD - acceptable + StringBuilder buffer = new StringBuilder(); + parseContents(reader, null, buffer); + String html = buffer.toString().trim(); + return html.isEmpty() ? null : MarkupLine.fromHtml(html); + } + + /** + * Parse a markup multiline from XHTML. + * + * @param reader + * the XML event stream reader + * @return the markup string + * @throws XMLStreamException + * if an error occurred while parsing + */ + public MarkupMultiline parseMarkupMultiline(XMLEventReader2 reader) throws XMLStreamException { + StringBuilder buffer = new StringBuilder(); + parseToString(reader, buffer); + String html = buffer.toString().trim(); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("XML->HTML: {}", html); + } + return html.isEmpty() ? null : MarkupMultiline.fromHtml(html); + } + + /** + * Parse a markup multiline from XHTML. + * + * @param reader + * the XML event stream reader + * @param buffer + * the markup string buffer + * @throws XMLStreamException + * if an error occurred while parsing + */ + private void parseToString(XMLEventReader2 reader, StringBuilder buffer) // NOPMD - acceptable + throws XMLStreamException { + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseToString(enter): {}", + // XmlEventUtil.toString(reader.peek())); + // } + + outer: while (reader.hasNextEvent() && !reader.peek().isEndElement()) { + // skip whitespace before the next block element + XMLEvent nextEvent = XmlEventUtil.skipWhitespace(reader); + + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseToString: {}", XmlEventUtil.toString(nextEvent)); + // } + + if (nextEvent.isStartElement()) { + StartElement start = nextEvent.asStartElement(); + QName name = start.getName(); + + // Note: the next element is not consumed. The called method is expected to + // consume it + if (BLOCK_ELEMENTS.contains(name.getLocalPart())) { + parseStartElement(reader, start, buffer); + + // the next event should be the event after the start's END_ELEMENT + // assert XmlEventUtil.isNextEventEndElement(reader, name) : + // XmlEventUtil.toString(reader.peek()); + } else { + // throw new IllegalStateException(); + // stop parsing on first unrecognized event + break outer; + } + } + // reader.nextEvent(); + + // skip whitespace before the next block element + XmlEventUtil.skipWhitespace(reader); + } + + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseToString(exit): {}", reader.peek() != null ? + // XmlEventUtil.toString(reader.peek()) : ""); + // } + } + + private void parseStartElement(XMLEventReader2 reader, StartElement start, StringBuilder buffer) + throws XMLStreamException { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("parseStartElement(enter): {}", XmlEventUtil.toString(start)); + } + + // consume the start event + reader.nextEvent(); + + QName name = start.getName(); + buffer.append('<') + .append(name.getLocalPart()); + for (Attribute attribute : CollectionUtil.toIterable( + ObjectUtils.notNull(start.getAttributes()))) { + buffer + .append(' ') + .append(attribute.getName().getLocalPart()) + .append("=\"") + .append(attribute.getValue()) + .append('"'); + } + + XMLEvent next = reader.peek(); + if (next != null && next.isEndElement()) { + buffer.append("/>"); + // consume end element event + reader.nextEvent(); + } else { + buffer.append('>'); + + // parse until the start's END_ELEMENT is reached + parseContents(reader, start, buffer); + + buffer + .append("'); + + // the next event should be the start's END_ELEMENT + XmlEventUtil.assertNext(reader, XMLStreamConstants.END_ELEMENT, name); + + // consume the start's END_ELEMENT + reader.nextEvent(); + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("parseStartElement(exit): {}", reader.peek() != null ? XmlEventUtil.toString(reader.peek()) : ""); + } + } + + private void parseContents(XMLEventReader2 reader, StartElement start, StringBuilder buffer) + throws XMLStreamException { + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseContents(enter): {}", + // XmlEventUtil.toString(reader.peek())); + // } + + XMLEvent event; + while (reader.hasNextEvent() && !(event = reader.peek()).isEndElement()) { + // // skip whitespace before the next list item + // event = XmlEventUtil.skipWhitespace(reader); + + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseContents(before): {}", XmlEventUtil.toString(event)); + // } + + if (event.isStartElement()) { + StartElement nextStart = event.asStartElement(); + // QName nextName = nextStart.getName(); + parseStartElement(reader, nextStart, buffer); + + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseContents(after): {}", + // XmlEventUtil.toString(reader.peek())); + // } + + // assert XmlEventUtil.isNextEventEndElement(reader, nextName) : + // XmlEventUtil.toString(reader.peek()); + + // reader.nextEvent(); + } else if (event.isCharacters()) { + Characters characters = event.asCharacters(); + buffer.append(Escaping.escapeHtml(characters.getData(), true)); + reader.nextEvent(); + } + } + + assert start == null + || XmlEventUtil.isEventEndElement(reader.peek(), ObjectUtils.notNull(start.getName())) : XmlEventUtil + .generateExpectedMessage(reader.peek(), XMLStreamConstants.END_ELEMENT, start.getName()); + + // if (LOGGER.isDebugEnabled()) { + // LOGGER.debug("parseContents(exit): {}", reader.peek() != null ? + // XmlEventUtil.toString(reader.peek()) : ""); + // } + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AbstractMarkupWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java similarity index 84% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AbstractMarkupWriter.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java index 056edf3ca..f6841f767 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AbstractMarkupWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java @@ -24,7 +24,7 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; // NOPMD AST processor has many members +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; // NOPMD AST processor has many members import com.vladsch.flexmark.ast.AutoLink; import com.vladsch.flexmark.ast.BlockQuote; @@ -64,6 +64,7 @@ import com.vladsch.flexmark.util.sequence.Escaping; import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.HtmlQuoteTagExtension.DoubleQuoteNode; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.IMarkupWriter; import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -85,6 +86,7 @@ import javax.xml.namespace.QName; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** @@ -105,16 +107,8 @@ public abstract class AbstractMarkupWriter // NOPMD not static { ENTITY_MAP = new HashMap<>(); - // special cases + // force writing of non-breaking spaces ENTITY_MAP.put("&npsb;", "&npsb;"); - // ENTITY_MAP.put(">", ">"); - // normal cases - // ENTITY_MAP.put("&", "&"); - /* - * ENTITY_MAP.put("‘", "‘"); ENTITY_MAP.put("’", "’"); ENTITY_MAP.put("…", "…"); - * ENTITY_MAP.put("—", "—"); ENTITY_MAP.put("–", "–"); ENTITY_MAP.put("“", "“"); - * ENTITY_MAP.put("”", "”"); ENTITY_MAP.put("«", "«"); ENTITY_MAP.put("»", "»"); - */ } @NonNull @@ -126,33 +120,47 @@ public abstract class AbstractMarkupWriter // NOPMD not @NonNull private final ListOptions options; - public AbstractMarkupWriter(@NonNull String namespace, @NonNull ListOptions options, @NonNull T stream) { + /** + * Construct a new markup writer. + * + * @param namespace + * the XHTML namespace to use for elements + * @param options + * list production options + * @param stream + * the stream to write to + */ + protected AbstractMarkupWriter(@NonNull String namespace, @NonNull ListOptions options, @NonNull T stream) { this.namespace = namespace; this.options = options; this.stream = stream; } @NonNull - protected String getNamespace() { + private String getNamespace() { return namespace; } - protected ListOptions getOptions() { + private ListOptions getOptions() { return options; } + /** + * Get the underlying stream to write to. + * + * @return the stream + */ @NonNull protected T getStream() { return stream; } - @Override @NonNull - public QName asQName(@NonNull String localName) { + private QName asQName(@NonNull String localName) { return new QName(getNamespace(), localName); } - protected void visitChildren( + private void visitChildren( @NonNull Node parentNode, @NonNull ChildHandler childHandler) throws E { for (Node node : parentNode.getChildren()) { @@ -161,7 +169,7 @@ protected void visitChildren( } } - protected void writePrecedingNewline(@NonNull Block node) throws E { + private void writePrecedingNewline(@NonNull Block node) throws E { Node prev = node.getPrevious(); if (prev != null || !(node.getParent() instanceof com.vladsch.flexmark.util.ast.Document)) { @@ -169,7 +177,7 @@ protected void writePrecedingNewline(@NonNull Block node) throws E { } } - protected void writeTrailingNewline(@NonNull Block node) throws E { + private void writeTrailingNewline(@NonNull Block node) throws E { Node next = node.getNext(); if (next != null && !next.isOrDescendantOfType(Block.class) // handled by preceding block || next == null && !(node.getParent() instanceof com.vladsch.flexmark.util.ast.Document)) { @@ -177,12 +185,35 @@ protected void writeTrailingNewline(@NonNull Block node) throws E { } } + /** + * Write an HTML element with the provided local name. + * + * @param localName + * the element name + * @param node + * the markup node related to the element + * @param attributes + * attributes associated with the element to also write + * @param childHandler + * a handler used to process child node content or {@code null} + * @throws E + * if an error occurred while writing the markup + */ @Override - public final void writeElement( - QName qname, - Node node, - Map attributes, - ChildHandler childHandler) throws E { + public void writeElement( + @NonNull String localName, + @NonNull Node node, + @NonNull Map attributes, + @Nullable ChildHandler childHandler) throws E { + QName qname = asQName(localName); + writeElement(qname, node, attributes, childHandler); + } + + private void writeElement( + @NonNull QName qname, + @NonNull Node node, + @NonNull Map attributes, + @Nullable ChildHandler childHandler) throws E { if (node.hasChildren()) { writeElementStart(qname, attributes); if (childHandler != null) { @@ -194,6 +225,65 @@ public final void writeElement( } } + @Override + public void writeEmptyElement( + @NonNull String localName, + @NonNull Map attributes) throws E { + QName qname = asQName(localName); + writeEmptyElement(qname, attributes); + } + + /** + * Write an empty element with the provided qualified name and attributes. + * + * @param qname + * the qualified name to use for the element name + * @param attributes + * the attributes + * @throws E + * if an error occurred while writing + */ + protected abstract void writeEmptyElement( + @NonNull QName qname, + @NonNull Map attributes) throws E; + + /** + * Write a start element with the provided qualified name and no attributes. + * + * @param qname + * the qualified name to use for the element name + * @throws E + * if an error occurred while writing + */ + private void writeElementStart( + @NonNull QName qname) throws E { + writeElementStart(qname, CollectionUtil.emptyMap()); + } + + /** + * Write a start element with the provided qualified name and attributes. + * + * @param qname + * the qualified name to use for the element name + * @param attributes + * the attributes + * @throws E + * if an error occurred while writing + */ + protected abstract void writeElementStart( + @NonNull QName qname, + @NonNull Map attributes) throws E; + + /** + * Write an end element with the provided qualified name. + * + * @param qname + * the qualified name to use for the element name + * @throws E + * if an error occurred while writing + */ + protected abstract void writeElementEnd(@NonNull QName qname) throws E; + @SuppressWarnings({ "unchecked", "unused", @@ -270,6 +360,14 @@ private void writeHtmlEntity(String entityText) throws E { } } + /** + * Write an HTML entity. + * + * @param text + * the entity text + * @throws E + * if an error occurred while writing + */ protected void writeHtmlEntityInternal(@NonNull String text) throws E { writeText(text); } @@ -656,7 +754,18 @@ public void writeBlockQuote(BlockQuote node, ChildHandler childHandler) th } @Override - public void writeList(QName qname, ListBlock node, ChildHandler listItemHandler) throws E { + public void writeList( + @NonNull String localName, + @NonNull ListBlock node, + @NonNull ChildHandler listItemHandler) throws E { + QName qname = asQName(localName); + writeList(qname, node, listItemHandler); + } + + private void writeList( + @NonNull QName qname, + @NonNull ListBlock node, + @NonNull ChildHandler listItemHandler) throws E { Map attributes = new LinkedHashMap<>(); // NOPMD local use; thread-safe if (node instanceof OrderedList) { OrderedList ol = (OrderedList) node; @@ -678,8 +787,9 @@ public void writeList(QName qname, ListBlock node, ChildHandler listItemHa writeTrailingNewline(node); } - @Override - public void writeListItem(ListItem node, ChildHandler listItemHandler) throws E { + private void writeListItem( + @NonNull ListItem node, + @NonNull ChildHandler listItemHandler) throws E { QName qname = asQName("li"); writePrecedingNewline(node); writeElementStart(qname); @@ -718,9 +828,17 @@ public void writeComment(HtmlCommentBlock node) throws E { } + /** + * Write a comment. + * + * @param text + * the comment text + * @throws E + * if an error occurred while writing + */ protected abstract void writeComment(@NonNull CharSequence text) throws E; - protected static class NodeVisitorException + private static class NodeVisitorException extends IllegalStateException { /** * the serial version uid. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/Date.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/Date.java index 7728a397b..290b67be4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/Date.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/Date.java @@ -1,54 +1,54 @@ -/* - * 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.datatype.object; - -import java.time.ZonedDateTime; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class Date // NOPMD - intentional - extends AbstractAmbiguousTemporal { - - /** - * Construct a new date object. This type supports ambiguous dates that were - * provided without a time zone. - * - * @param value - * the date value - * @param hasTimeZone - * {@code true} if the date is intended to have an associated time zone - * or {@code false} otherwise - */ - public Date(@NonNull ZonedDateTime value, boolean hasTimeZone) { - super(value, hasTimeZone); - } - - @Override - public Date copy() { - return new Date(getValue(), hasTimeZone()); - } -} +/* + * 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.datatype.object; + +import java.time.ZonedDateTime; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class Date // NOPMD - intentional + extends AbstractAmbiguousTemporal { + + /** + * Construct a new date object. This type supports ambiguous dates that were + * provided without a time zone. + * + * @param value + * the date value + * @param hasTimeZone + * {@code true} if the date is intended to have an associated time zone + * or {@code false} otherwise + */ + public Date(@NonNull ZonedDateTime value, boolean hasTimeZone) { + super(value, hasTimeZone); + } + + @Override + public Date copy() { + return new Date(getValue(), hasTimeZone()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/DateTime.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/DateTime.java index e3bd4593e..5c48fc262 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/DateTime.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/DateTime.java @@ -1,54 +1,54 @@ -/* - * 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.datatype.object; - -import java.time.ZonedDateTime; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public class DateTime - extends AbstractAmbiguousTemporal { - - /** - * Construct a new date/time object. This type supports ambiguous dates/times - * that were provided without a time zone. - * - * @param value - * the date/time value - * @param hasTimeZone - * {@code true} if the date/time is intended to have an associated time - * zone or {@code false} otherwise - */ - public DateTime(@NonNull ZonedDateTime value, boolean hasTimeZone) { - super(value, hasTimeZone); - } - - @Override - public DateTime copy() { - return new DateTime(getValue(), hasTimeZone()); - } -} +/* + * 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.datatype.object; + +import java.time.ZonedDateTime; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class DateTime + extends AbstractAmbiguousTemporal { + + /** + * Construct a new date/time object. This type supports ambiguous dates/times + * that were provided without a time zone. + * + * @param value + * the date/time value + * @param hasTimeZone + * {@code true} if the date/time is intended to have an associated time + * zone or {@code false} otherwise + */ + public DateTime(@NonNull ZonedDateTime value, boolean hasTimeZone) { + super(value, hasTimeZone); + } + + @Override + public DateTime copy() { + return new DateTime(getValue(), hasTimeZone()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java index 64b1b3399..5b822836e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/AbstractCodedMetapathException.java @@ -97,7 +97,7 @@ public String getMessage() { * * @return the error code value */ - protected int getCode() { + public int getCode() { return code; } @@ -106,7 +106,7 @@ protected int getCode() { * * @return the error code family */ - protected abstract String getCodePrefix(); + public abstract String getCodePrefix(); /** * Get a combination of the error code family and value. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java index 0b13b5479..9069ed041 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicContext.java @@ -30,8 +30,8 @@ import gov.nist.secauto.metaschema.core.configuration.IConfiguration; import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration; import gov.nist.secauto.metaschema.core.metapath.function.DefaultFunction.CallingContext; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction.FunctionProperty; import gov.nist.secauto.metaschema.core.metapath.item.node.IDocumentNodeItem; -import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.model.IUriResolver; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -45,7 +45,10 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; // TODO: add support for in-scope namespaces /** @@ -54,17 +57,24 @@ */ public class DynamicContext { // NOPMD - intentional data class @NonNull - private final Map> letVariableMap; + private final Map> letVariableMap; @NonNull private final SharedState sharedState; /** - * Construct a new Metapath dynamic context. + * Construct a new dynamic context with a default static context. + */ + public DynamicContext() { + this(StaticContext.instance()); + } + + /** + * Construct a new Metapath dynamic context using the provided static context. * * @param staticContext * the Metapath static context */ - DynamicContext(@NonNull StaticContext staticContext) { + public DynamicContext(@NonNull StaticContext staticContext) { this.letVariableMap = new ConcurrentHashMap<>(); this.sharedState = new SharedState(staticContext); } @@ -83,7 +93,9 @@ private static class SharedState { private final ZonedDateTime currentDateTime; @NonNull private final Map availableDocuments; + @NonNull private final Map> functionResultCache; + @Nullable private CachingLoader documentLoader; @NonNull private final IMutableConfiguration> configuration; @@ -103,11 +115,12 @@ public SharedState(@NonNull StaticContext staticContext) { } /** - * Generate a new dynamic context that is based on this object. + * Generate a new dynamic context that is a copy of this dynamic context. *

* This method can be used to create a new sub-context where changes can be made * without affecting this context. This is useful for setting information that - * is only used in a limited evaluation scope, such as variables. + * is only used in a limited evaluation sub-scope, such as for handling variable + * assignment. * * @return a new dynamic context */ @@ -146,43 +159,137 @@ public ZonedDateTime getCurrentDateTime() { return sharedState.currentDateTime; } + /** + * Get the mapping of loaded documents from the document URI to the document + * node. + * + * @return the map of document URIs to document nodes + */ @SuppressWarnings("null") @NonNull - public Map getAvailableDocuments() { + public Map getAvailableDocuments() { return Collections.unmodifiableMap(sharedState.availableDocuments); } + /** + * Get the document loader assigned to this dynamic context. + * + * @return the loader + * @throws DynamicMetapathException + * with an error code + * {@link DynamicMetapathException#DYNAMIC_CONTEXT_ABSENT} if a + * document loader is not configured for this dynamic context + */ + @NonNull public IDocumentLoader getDocumentLoader() { - return sharedState.documentLoader; + IDocumentLoader retval = sharedState.documentLoader; + if (retval == null) { + throw new DynamicMetapathException(DynamicMetapathException.DYNAMIC_CONTEXT_ABSENT, + "No document loader configured for the dynamic context."); + } + return retval; } + /** + * Assign a document loader to this dynamic context. + * + * @param documentLoader + * the document loader to assign + */ public void setDocumentLoader(@NonNull IDocumentLoader documentLoader) { this.sharedState.documentLoader = new CachingLoader(documentLoader); } + /** + * Get the cached function call result for evaluating a function that has the + * property {@link FunctionProperty#DETERMINISTIC}. + * + * @param callingContext + * the function calling context information that distinguishes the call + * from any other call + * @return the cached result sequence for the function call + */ + @Nullable public ISequence getCachedResult(@NonNull CallingContext callingContext) { return sharedState.functionResultCache.get(callingContext); } + /** + * Cache a function call result for a that has the property + * {@link FunctionProperty#DETERMINISTIC}. + * + * @param callingContext + * the calling context information that distinguishes the call from any + * other call + * @param result + * the function call result + */ + public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequence result) { + ISequence old = sharedState.functionResultCache.put(callingContext, result); + assert old == null; + } + + /** + * Used to disable the evaluation of predicate expressions during Metapath + * evaluation. + *

+ * This can be useful for determining the potential targets identified by a + * Metapath expression as a partial evaluation, without evaluating that these + * targets match the predicate. + * + * @return this dynamic context + */ @NonNull public DynamicContext disablePredicateEvaluation() { this.sharedState.configuration.disableFeature(MetapathEvaluationFeature.METAPATH_EVALUATE_PREDICATES); return this; } + /** + * Used to enable the evaluation of predicate expressions during Metapath + * evaluation. + *

+ * This is the default behavior if unchanged. + * + * @return this dynamic context + */ @NonNull - public IConfiguration> getConfiguration() { - return sharedState.configuration; + public DynamicContext enablePredicateEvaluation() { + this.sharedState.configuration.enableFeature(MetapathEvaluationFeature.METAPATH_EVALUATE_PREDICATES); + return this; } - public void cacheResult(@NonNull CallingContext callingContext, @NonNull ISequence result) { - ISequence old = sharedState.functionResultCache.put(callingContext, result); - assert old == null; + /** + * Get the Metapath evaluation configuration. + * + * @return the configuration + */ + @NonNull + public IConfiguration> getConfiguration() { + return sharedState.configuration; } + /** + * Get the sequence value assigned to a let variable with the provided qualified + * name. + * + * @param name + * the variable qualified name + * @return the non-null variable value + * @throws MetapathException + * of the variable has not been assigned or if the variable value is + * {@code null} + */ @NonNull - public ISequence getVariableValue(@NonNull String name) { - return ObjectUtils.requireNonNull(letVariableMap.get(name)); + public ISequence getVariableValue(@NonNull QName name) { + ISequence retval = letVariableMap.get(name); + if (retval == null) { + if (!letVariableMap.containsKey(name)) { + throw new MetapathException(String.format("Variable '%s' not defined in context.", name)); + } + throw new MetapathException(String.format("Variable '%s' has null contents.", name)); + } + return retval; } /** @@ -193,7 +300,7 @@ public ISequence getVariableValue(@NonNull String name) { * @param boundValue * the value to bind to the variable */ - public void bindVariableValue(@NonNull String name, @NonNull ISequence boundValue) { + public void bindVariableValue(@NonNull QName name, @NonNull ISequence boundValue) { letVariableMap.put(name, boundValue); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java index df5f8fcc0..2b432fad5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/DynamicMetapathException.java @@ -105,7 +105,7 @@ public DynamicMetapathException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "MPDY"; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EQNameUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EQNameUtils.java new file mode 100644 index 000000000..7f0201820 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EQNameUtils.java @@ -0,0 +1,159 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class EQNameUtils { + private static final Pattern URI_QUALIFIED_NAME = Pattern.compile("^Q\\{([^{}]*)\\}(.+)$"); + private static final Pattern LEXICAL_NAME = Pattern.compile("^(?:([^:]+):)?(.+)$"); + private static final Pattern NCNAME = Pattern.compile(String.format("^(\\p{L}|_)(\\p{L}|\\p{N}|[.\\-_])*$")); + + private EQNameUtils() { + // disable construction + } + + /** + * Parse a name as a qualified name. + *

+ * The name can be: + *

    + *
  • A URI qualified name of the form Q{URI}name, where the URI + * represents the namespace
  • + *
  • A lexical name of the forms prefix:name or + * name, where the prefix represents the namespace
  • + *
+ * + * @param name + * the name to parse + * @param resolver + * the prefix resolver to use to determine the namespace for a given + * prefix + * @return the parsed qualified name + */ + @NonNull + public static QName parseName( + @NonNull String name, + @NonNull IEQNamePrefixResolver resolver) { + Matcher matcher = URI_QUALIFIED_NAME.matcher(name); + return matcher.matches() + ? newUriQualifiedName(matcher) + : parseLexicalQName(name, resolver); + } + + /** + * Parse a URI qualified name. + *

+ * The name is expected to be a URI qualified name of the form + * {URI}name, where the URI represents the namespace. + * + * @param name + * the name to parse + * @return the parsed qualified name + */ + @NonNull + public static QName parseUriQualifiedName(@NonNull String name) { + Matcher matcher = URI_QUALIFIED_NAME.matcher(name); + if (!matcher.matches()) { + throw new IllegalArgumentException( + String.format("The name '%s' is not a valid BracedURILiteral of the form: Q{URI}local-name", name)); + } + return newUriQualifiedName(matcher); + } + + @NonNull + private static QName newUriQualifiedName(@NonNull Matcher matcher) { + return new QName(matcher.group(1), matcher.group(2)); + } + + /** + * Parse a lexical name as a qualified name. + *

+ * The name is expected to be a lexical name of the forms + * prefix:name or name, where the prefix represents + * the namespace. + * + * @param name + * the name to parse + * @param resolver + * the prefix resolver to use to determine the namespace for a given + * prefix + * @return the parsed qualified name + */ + @NonNull + public static QName parseLexicalQName( + @NonNull String name, + @NonNull IEQNamePrefixResolver resolver) { + Matcher matcher = LEXICAL_NAME.matcher(name); + if (!matcher.matches()) { + throw new IllegalArgumentException( + String.format("The name '%s' is not a valid lexical QName of the form: prefix:local-name or local-name", + name)); + } + String prefix = matcher.group(1); + + if (prefix == null) { + prefix = XMLConstants.DEFAULT_NS_PREFIX; + } + + String namespace = resolver.resolve(prefix); + return new QName(namespace, matcher.group(2), prefix); + } + + /** + * Determine if the name is a non-colonized name. + * + * @param name + * the name to test + * @return {@code true} if the name is not colonized, or {@code false} otherwise + */ + public static boolean isNcName(@NonNull String name) { + return NCNAME.matcher(name).matches(); + } + + /** + * Provides a callback for resolving namespace prefixes. + */ + @FunctionalInterface + public interface IEQNamePrefixResolver { + /** + * Get the URI string for the provided namespace prefix. + * + * @param prefix + * the namespace prefix + * @return the URI string + */ + @NonNull + String resolve(@NonNull String prefix); + } +} diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/AbstractBoundAnnotatedJavaField.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java similarity index 61% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/AbstractBoundAnnotatedJavaField.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java index b5a38f1da..e3d330b4a 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/AbstractBoundAnnotatedJavaField.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java @@ -24,55 +24,51 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.impl; +package gov.nist.secauto.metaschema.core.metapath; -import gov.nist.secauto.metaschema.databind.model.IFeatureJavaField; -import gov.nist.secauto.metaschema.databind.model.annotations.ModelUtil; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; +import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; -public abstract class AbstractBoundAnnotatedJavaField implements IFeatureJavaField { - @NonNull - private final Field javaField; - @NonNull - private final A annotation; - +public interface ICollectionValue { /** - * Construct a new binding between a Java field and a binding Java annotation. + * Get the collection value as a sequence. + *

+ * If the collection value is a sequence, then the sequence is returned. * - * @param javaField - * the bound Java field - * @param annotationClass - * the binding annotation Java class + * @return the resulting sequence */ - protected AbstractBoundAnnotatedJavaField( - @NonNull Field javaField, - @NonNull Class annotationClass) { - this.javaField = javaField; - this.annotation = ModelUtil.getAnnotation(javaField, annotationClass); - } + // TODO: rename to toSequence and resolve conflicting methods? + @NonNull + ISequence asSequence(); /** - * Get the bound Java field. + * Get the stream of items for the collection value. + *

+ * If the collection value is a sequence, then the items in the collection are + * returned. * - * @return the bound Java field + * @param value + * the collection value + * @return the sequence of related items */ - @Override @NonNull - public Field getField() { - return javaField; + static Stream normalizeAsItems(@NonNull ICollectionValue value) { + return value instanceof IItem + ? ObjectUtils.notNull(Stream.of((IItem) value)) + : value.asSequence().stream(); } /** - * Get the binding Java annotation. + * Get the stream of items for the collection value. + *

+ * If the collection value contains items, then these items are returned. * - * @return the binding Java annotation + * @return a stream of related items */ @NonNull - public A getAnnotation() { - return annotation; - } + Stream flatten(); } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBinding.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java similarity index 88% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBinding.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java index fffff52dd..0cae7cbcc 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBinding.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java @@ -24,16 +24,16 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.metaschema; - -import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +package gov.nist.secauto.metaschema.core.metapath; import edu.umd.cs.findbugs.annotations.NonNull; -public interface IBinding { - @NonNull - Object getBinding(); - +public interface IPrintable { + /** + * Get the string value. + * + * @return the string value + */ @NonNull - INodeItem getBoundNodeItem(); + String asString(); } 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 569f3ccac..145df6011 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 @@ -26,20 +26,22 @@ package gov.nist.secauto.metaschema.core.metapath; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractSequence; +import gov.nist.secauto.metaschema.core.metapath.impl.SequenceN; +import gov.nist.secauto.metaschema.core.metapath.impl.SingletonSequence; +import gov.nist.secauto.metaschema.core.metapath.impl.StreamSequence; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; @@ -54,125 +56,142 @@ * Items is a sequence are typically ordered based on their position in the * original node graph based on a depth first ordering. * - * @param + * @param * the Java type of the items in a sequence */ -public interface ISequence extends List { - @SuppressWarnings("rawtypes") - ISequence EMPTY = new EmptyListImpl<>(); - +@SuppressWarnings("PMD.ShortMethodName") +public interface ISequence extends List, IPrintable, ICollectionValue { /** * Get an empty sequence. * - * @param + * @param * the item type * @return the empty sequence */ - @SuppressWarnings({ "unchecked", "null" }) + @SuppressWarnings("null") @NonNull - static ISequence empty() { - return EMPTY; + static ISequence empty() { + return AbstractSequence.empty(); + } + + @Override + default Iterator iterator() { + return getValue().listIterator(); } /** - * Construct a new sequence containing the provided {@code item}. - *

- * If the item is {@code null} and empty sequence will be created. + * Get the items in this sequence as a {@link List}. * - * @param - * the type of items contained in the sequence. - * @param item - * the item to add to the sequence - * @return the new sequence + * @return a list containing all the items of the sequence */ @NonNull - static ISequence of( // NOPMD - intentional - @Nullable ITEM_TYPE item) { - ISequence retval; - if (item == null) { - retval = empty(); - } else { - retval = new SingletonSequenceImpl<>(item); - } - return retval; - } + List getValue(); /** - * Construct a new sequence containing the provided {@code items}. + * Get the items in this sequence as a {@link Stream}. * - * @param - * the type of items contained in the sequence. - * @param items - * the items to add to the sequence - * @return the new sequence + * @return a stream containing all the items of the sequence */ - @SafeVarargs + @Override @NonNull - static ISequence of( // NOPMD - intentional - @NonNull ITEM_TYPE... items) { - return of(ObjectUtils.notNull(Arrays.asList(items))); - } + Stream stream(); /** - * Construct a new sequence containing the provided {@code items}. + * Retrieves the first item in a sequence. + *

+ * If the sequence is empty, a {@code null} result is returned. If + * requireSingleton is {@code true} and the sequence contains more than one + * item, a {@link TypeMetapathException} is thrown. * - * @param - * the type of items contained in the sequence. + * @param + * the item type to return derived from the provided sequence * @param items - * the items to add to the sequence - * @return the new sequence + * the sequence to retrieve the first item from + * @param requireSingleton + * if {@code true} then a {@link TypeMetapathException} is thrown if + * the sequence contains more than one item + * @return {@code null} if the sequence is empty, or the item otherwise + * @throws TypeMetapathException + * if the sequence contains more than one item and requireSingleton is + * {@code true} */ - @NonNull - static ISequence of( // NOPMD - intentional - @NonNull List items) { - ISequence retval; - if (items.isEmpty()) { - retval = empty(); - } else if (items.size() == 1) { - retval = new SingletonSequenceImpl<>(ObjectUtils.notNull(items.iterator().next())); - } else { - retval = new ListSequenceImpl<>(items); - } - return retval; + static T getFirstItem(@NonNull ISequence items, boolean requireSingleton) { + return getFirstItem(items.stream(), requireSingleton); } /** - * Construct a new sequence containing the provided {@code items}. + * Retrieves the first item in a stream of items. + *

+ * If the sequence is empty, a {@code null} result is returned. If + * requireSingleton is {@code true} and the sequence contains more than one + * item, a {@link TypeMetapathException} is thrown. * - * @param - * the type of items contained in the sequence. + * @param + * the item type to return derived from the provided sequence * @param items - * the items to add to the sequence - * @return the new sequence + * the sequence to retrieve the first item from + * @param requireSingleton + * if {@code true} then a {@link TypeMetapathException} is thrown if + * the sequence contains more than one item + * @return {@code null} if the sequence is empty, or the item otherwise + * @throws TypeMetapathException + * if the sequence contains more than one item and requireSingleton is + * {@code true} */ - // TODO: remove null check on callers - @NonNull - static ISequence of( // NOPMD - intentional - Stream items) { - return items == null ? empty() : new StreamSequenceImpl<>(items); - } - - @Override - default Iterator iterator() { - return asList().listIterator(); + static T getFirstItem(@NonNull Stream items, boolean requireSingleton) { + return items.limit(2) + .reduce((t, u) -> { + if (requireSingleton) { + throw new InvalidTypeMetapathException( + null, + String.format("sequence expected to contain only one item, but found multiple")); + } + return t; + }).orElse(null); } /** - * Get the items in this sequence as a {@link List}. + * Retrieves the first item in this sequence. + *

+ * If the sequence is empty, a {@code null} result is returned. If + * requireSingleton is {@code true} and the sequence contains more than one + * item, a {@link TypeMetapathException} is thrown. * - * @return a list containing all the items of the sequence + * @param requireSingleton + * if {@code true} then a {@link TypeMetapathException} is thrown if + * the sequence contains more than one item + * @return {@code null} if the sequence is empty, or the item otherwise + * @throws TypeMetapathException + * if the sequence contains more than one item and requireSingleton is + * {@code true} */ - @NonNull - List asList(); + @Nullable + default ITEM getFirstItem(boolean requireSingleton) { + return getFirstItem(this, requireSingleton); + } /** - * Get the items in this sequence as a {@link Stream}. + * Get this sequence as a collection value. * - * @return a stream containing all the items of the sequence + * @return the collection value */ - // TODO: rename to "stream" @NonNull - Stream asStream(); + default ICollectionValue toCollectionValue() { + ICollectionValue retval; + switch (size()) { + case 0: + retval = empty(); + break; + case 1: + // get the singleton item + retval = ObjectUtils.notNull(stream().findFirst().get()); + break; + default: + // get this sequence of 2 or more items + retval = this; + } + return retval; + } /** * Get a stream guaranteed to be backed by a list. @@ -180,46 +199,16 @@ default Iterator iterator() { * @return the stream */ @NonNull - default Stream safeStream() { - return ObjectUtils.notNull(asList().stream()); + default Stream safeStream() { + return ObjectUtils.notNull(getValue().stream()); } - /** - * This optional operation ensures that a list is used to back this sequence. - *

- * If a stream is currently backing this sequence, the stream will be collected - * into a list. This ensures the sequence can be visited multiple times. - * - * @return the resulting sequence - */ - @NonNull - ISequence collect(); - - /** - * Determine if this sequence is empty. - * - * @return {@code true} if the sequence contains no items, or {@code false} - * otherwise - */ - @Override - boolean isEmpty(); - - /** - * Get the count of items in this sequence. - * - * @return the count of items - */ + @SuppressWarnings("null") @Override - int size(); - - /** - * Iterate over each item in the sequence using the provided {@code action}. - * - * @param action - * code to execute for each item - */ - @Override - void forEach(Consumer action); + default Stream flatten() { + // TODO: Is a safe stream needed here? + return safeStream(); + } /** * A {@link Collector} implementation to generates a sequence from a stream of @@ -240,7 +229,7 @@ public Supplier> supplier() { @Override public BiConsumer, ITEM_TYPE> accumulator() { - return (list, value) -> list.add(value); + return List::add; } @Override @@ -253,7 +242,7 @@ public BinaryOperator> combiner() { @Override public Function, ISequence> finisher() { - return list -> of(ObjectUtils.notNull(list)); + return list -> ofCollection(ObjectUtils.notNull(list)); } @Override @@ -263,6 +252,11 @@ public Set characteristics() { }; } + @Override + default ISequence asSequence() { + return this; + } + /** * Apply the provided {@code mapFunction} to each item in the sequence. * @@ -280,107 +274,352 @@ static ISequence map( @NonNull Function mapFunction, @NonNull ISequence seq) { return seq.safeStream() - .map(item -> mapFunction.apply(item)) + .map(mapFunction::apply) .collect(toSequence()); } - @Override - default boolean contains(Object obj) { - return asList().contains(obj); - } - - @Override - default Object[] toArray() { - return asList().toArray(); - } - - @Override - default T[] toArray(T[] array) { - return asList().toArray(array); - } - - @Override - default boolean add(ITEM_TYPE item) { - throw new UnsupportedOperationException("object is immutable"); - } - - @Override - default void add(int index, ITEM_TYPE element) { - throw new UnsupportedOperationException("object is immutable"); - } - - @Override - default boolean addAll(Collection collection) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing the provided {@code items}. + * + * @param + * the type of items contained in the sequence. + * @param items + * the items to add to the sequence + * @return the new sequence + */ + @NonNull + static ISequence ofCollection( // NOPMD - intentional + @NonNull List items) { + ISequence retval; + if (items.isEmpty()) { + retval = empty(); + } else if (items.size() == 1) { + retval = new SingletonSequence<>(ObjectUtils.notNull(items.iterator().next())); + } else { + retval = new SequenceN<>(items); + } + return retval; } - @Override - default boolean addAll(int index, Collection collection) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing the provided {@code item}. + *

+ * If the item is {@code null} and empty sequence will be created. + * + * @param + * the type of items contained in the sequence. + * @param item + * the item to add to the sequence + * @return the new sequence + */ + @NonNull + static ISequence of( // NOPMD - intentional + @Nullable T item) { + return item == null ? empty() : new SingletonSequence<>(item); } - @Override - default boolean remove(Object obj) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing the provided {@code items}. + * + * @param + * the type of items contained in the sequence. + * @param items + * the items to add to the sequence + * @return the new sequence + */ + // TODO: remove null check on callers + @NonNull + static ISequence of(@NonNull Stream items) { + return new StreamSequence<>(items); } - @Override - default ITEM_TYPE remove(int index) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing zero elements. + * + * @param + * the item type + * @return an empty {@code ISequence} + */ + @NonNull + static ISequence of() { + return empty(); } - @Override - default boolean containsAll(Collection collection) { - return asList().containsAll(collection); + /** + * Returns an unmodifiable sequence containing two items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2) { + return new SequenceN<>(e1, e2); } - @Override - default boolean removeAll(Collection collection) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing three elements. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3) { + return new SequenceN<>(e1, e2, e3); } - @Override - default boolean retainAll(Collection collection) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing four items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4) { + return new SequenceN<>(e1, e2, e3, e4); } - @Override - default void clear() { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing five items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5) { + return new SequenceN<>(e1, e2, e3, e4, e5); } - @Override - default ITEM_TYPE get(int index) { - return asList().get(index); + /** + * Returns an unmodifiable sequence containing six items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @param e6 + * the sixth item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5, T e6) { + return new SequenceN<>(e1, e2, e3, e4, e5, e6); } - @Override - default ITEM_TYPE set(int index, ITEM_TYPE element) { - throw new UnsupportedOperationException("object is immutable"); + /** + * Returns an unmodifiable sequence containing seven items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @param e6 + * the sixth item + * @param e7 + * the seventh item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5, T e6, T e7) { + return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7); } - @Override - default int indexOf(Object obj) { - return asList().indexOf(obj); + /** + * Returns an unmodifiable sequence containing eight items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @param e6 + * the sixth item + * @param e7 + * the seventh item + * @param e8 + * the eighth item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8) { + return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8); } - @Override - default int lastIndexOf(Object obj) { - return asList().lastIndexOf(obj); + /** + * Returns an unmodifiable sequence containing nine items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @param e6 + * the sixth item + * @param e7 + * the seventh item + * @param e8 + * the eighth item + * @param e9 + * the ninth item + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9) { + return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9); } - @Override - default ListIterator listIterator() { - return asList().listIterator(); + /** + * Returns an unmodifiable sequence containing ten items. + * + * @param + * the {@code ISequence}'s item type + * @param e1 + * the first item + * @param e2 + * the second item + * @param e3 + * the third item + * @param e4 + * the fourth item + * @param e5 + * the fifth item + * @param e6 + * the sixth item + * @param e7 + * the seventh item + * @param e8 + * the eighth item + * @param e9 + * the ninth item + * @param e10 + * the tenth item + * @return an {@code IArrayItem} containing the specified items + * @throws NullPointerException + * if an item is {@code null} + */ + @NonNull + static ISequence of(T e1, T e2, T e3, T e4, T e5, T e6, T e7, T e8, T e9, T e10) { + return new SequenceN<>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); } - @Override - default ListIterator listIterator(int index) { - return asList().listIterator(index); + /** + * Returns an unmodifiable sequence containing an arbitrary number of items. + * + * @param + * the {@code ISequence}'s item type + * @param items + * the items to be contained in the list + * @return an {@code ISequence} containing the specified items + * @throws NullPointerException + * if an item is {@code null} or if the array is {@code null} + */ + @SafeVarargs + @NonNull + static ISequence of(@NonNull T... items) { + return items.length == 0 ? empty() : new SequenceN<>(items); } - @Override - default List subList(int fromIndex, int toIndex) { - return asList().subList(fromIndex, toIndex); + /** + * Returns an unmodifiable sequence containing the items of the given + * Collection, in its iteration order. The given Collection must not be null, + * and it must not contain any null items. If the given Collection is + * subsequently modified, the returned array item will not reflect such + * modifications. + * + * @param + * the {@code ISequence}'s item type + * @param collection + * a {@code Collection} from which items are drawn, must be non-null + * @return an {@code ISequence} containing the items of the given + * {@code Collection} + * @throws NullPointerException + * if collection is null, or if it contains any nulls + * @since 10 + */ + @SuppressWarnings("unchecked") + @NonNull + static ISequence copyOf(Collection collection) { + return collection instanceof IArrayItem + ? (ISequence) collection + : collection.isEmpty() + ? empty() + : new SequenceN<>(new ArrayList<>(collection)); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTypeMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTypeMetapathException.java index f7ace0bbf..b08d9f5a3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTypeMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/InvalidTypeMetapathException.java @@ -56,7 +56,7 @@ public class InvalidTypeMetapathException * the original exception cause */ public InvalidTypeMetapathException(@NonNull IItem item, @NonNull Throwable cause) { - super(TypeMetapathException.INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName()), + super(INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName()), cause); this.item = item; } @@ -69,7 +69,7 @@ public InvalidTypeMetapathException(@NonNull IItem item, @NonNull Throwable caus * the item related to the invalid type error */ public InvalidTypeMetapathException(@NonNull IItem item) { - super(TypeMetapathException.INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName())); + super(INVALID_TYPE_ERROR, String.format("Invalid data type '%s'", item.getClass().getName())); this.item = item; } @@ -85,7 +85,7 @@ public InvalidTypeMetapathException(@NonNull IItem item) { * the original exception cause */ public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String message, @NonNull Throwable cause) { - super(TypeMetapathException.INVALID_TYPE_ERROR, message, cause); + super(INVALID_TYPE_ERROR, message, cause); this.item = item; } @@ -99,7 +99,7 @@ public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String messa * the exception message */ public InvalidTypeMetapathException(@Nullable IItem item, @Nullable String message) { - super(TypeMetapathException.INVALID_TYPE_ERROR, message); + super(INVALID_TYPE_ERROR, message); this.item = item; } 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 86b73dd12..1ddc29efb 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 @@ -50,21 +50,30 @@ public final class MetapathConstants { URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions")); @NonNull public static final URI NS_METAPATH_FUNCTIONS_MATH = ObjectUtils.requireNonNull( - URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions/math")); + URI.create(NS_METAPATH_FUNCTIONS + "/math")); @NonNull - public static final URI NS_METAPATH_FUNCTIONS_EXTENDED = NS_METAPATH; + public static final URI NS_METAPATH_FUNCTIONS_ARRAY = ObjectUtils.requireNonNull( + URI.create(NS_METAPATH_FUNCTIONS + "/array")); + @NonNull + public static final URI NS_METAPATH_FUNCTIONS_MAP = ObjectUtils.requireNonNull( + URI.create(NS_METAPATH_FUNCTIONS + "/map")); + @NonNull + public static final URI NS_METAPATH_FUNCTIONS_EXTENDED = NS_METAPATH_FUNCTIONS; @NonNull - public static final String PREFIX_METAPATH = "mp"; + public static final String PREFIX_METAPATH = "meta"; @NonNull public static final String PREFIX_XML_SCHEMA = "xs"; @NonNull public static final String PREFIX_XPATH_FUNCTIONS = "mp"; @NonNull 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 15d1674aa..61a74e031 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 @@ -32,8 +32,8 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.ParseTreePrinter; import gov.nist.secauto.metaschema.core.metapath.cst.BuildCSTVisitor; import gov.nist.secauto.metaschema.core.metapath.cst.CSTPrinter; -import gov.nist.secauto.metaschema.core.metapath.cst.ContextItem; import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; +import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem; import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.library.FnBoolean; import gov.nist.secauto.metaschema.core.metapath.function.library.FnData; @@ -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; @@ -88,17 +90,25 @@ public enum ResultType { /** * The result is expected to be an {@link INodeItem} value. */ - NODE; + // TODO: audit use of this value, replace with ITEM where appropriate + NODE, + /** + * The result is expected to be an {@link IItem} value. + */ + ITEM; } private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class); @NonNull - public static final MetapathExpression CONTEXT_NODE = new MetapathExpression(".", ContextItem.instance()); + public static final MetapathExpression CONTEXT_NODE + = new MetapathExpression(".", ContextItem.instance(), StaticContext.instance()); private final String path; @NonNull - private final IExpression node; + private final IExpression expression; + @NonNull + private final StaticContext staticContext; /** * Compiles a Metapath expression string. @@ -111,6 +121,24 @@ public enum ResultType { */ @NonNull public static MetapathExpression compile(@NonNull String path) { + StaticContext context = StaticContext.builder().build(); + + return compile(path, context); + } + + /** + * Compiles a Metapath expression string using the provided static context. + * + * @param path + * the metapath expression + * @param context + * the static evaluation context + * @return the compiled expression object + * @throws MetapathException + * if an error occurred while compiling the Metapath expression + */ + @NonNull + public static MetapathExpression compile(@NonNull String path, @NonNull StaticContext context) { @NonNull MetapathExpression retval; if (".".equals(path)) { retval = CONTEXT_NODE; @@ -124,6 +152,13 @@ public static MetapathExpression compile(@NonNull String path) { 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()); @@ -140,12 +175,12 @@ public static MetapathExpression compile(@NonNull String path) { } } - IExpression expr = new BuildCSTVisitor().visit(tree); + IExpression expr = new BuildCSTVisitor(context).visit(tree); if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log(String.format("Metapath CST:%n%s", CSTPrinter.toString(expr))); } - retval = new MetapathExpression(path, expr); + retval = new MetapathExpression(path, expr, context); } catch (MetapathException | ParseCancellationException ex) { String msg = String.format("Unable to compile Metapath '%s'", path); LOGGER.atError().withThrowable(ex).log(msg); @@ -162,10 +197,13 @@ public static MetapathExpression compile(@NonNull String path) { * the Metapath as a string * @param expr * the Metapath as a compiled abstract syntax tree (AST) + * @param staticContext + * the static evaluation context */ - protected MetapathExpression(@NonNull String path, @NonNull IExpression expr) { + protected MetapathExpression(@NonNull String path, @NonNull IExpression expr, @NonNull StaticContext staticContext) { this.path = path; - this.node = expr; + this.expression = expr; + this.staticContext = staticContext; } /** @@ -184,7 +222,27 @@ public String getPath() { */ @NonNull protected IExpression getASTNode() { - return node; + return expression; + } + + /** + * Get the static context used to compile this Metapath. + * + * @return the static context + */ + @NonNull + protected StaticContext getStaticContext() { + return staticContext; + } + + /** + * Generate a new dynamic context. + * + * @return the generated dynamic context + */ + @NonNull + public DynamicContext dynamicContext() { + return new DynamicContext(getStaticContext()); } @Override @@ -298,7 +356,7 @@ public T evaluateAs( * if the provided sequence is incompatible with the requested result * type */ - @SuppressWarnings("PMD.NullAssignment") // for readability + @SuppressWarnings({ "PMD.NullAssignment", "PMD.CyclomaticComplexity" }) // for readability @Nullable protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType resultType) { Object result; @@ -306,8 +364,9 @@ protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType case BOOLEAN: result = FnBoolean.fnBoolean(sequence).toBoolean(); break; + case ITEM: case NODE: - result = FunctionUtils.getFirstItem(sequence, true); + result = sequence.getFirstItem(true); break; case NUMBER: INumericItem numeric = FunctionUtils.toNumeric(sequence, true); @@ -317,8 +376,8 @@ protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType result = sequence; break; case STRING: - IItem item = FunctionUtils.getFirstItem(sequence, true); - result = item == null ? "" : FnData.fnDataItem(item).asString(); + IAnyAtomicItem item = FnData.fnData(sequence).getFirstItem(true); + result = item == null ? "" : item.asString(); break; default: throw new InvalidTypeMetapathException(null, String.format("unsupported result type '%s'", resultType.name())); @@ -360,10 +419,7 @@ public ISequence evaluate() { @NonNull public ISequence evaluate( @Nullable IItem focus) { - return (ISequence) evaluate( - focus, - StaticContext.builder() - .build().dynamicContext()); + return (ISequence) evaluate(focus, dynamicContext()); } /** 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 c30b41349..252d567c3 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 @@ -26,11 +26,16 @@ package gov.nist.secauto.metaschema.core.metapath; +import gov.nist.secauto.metaschema.core.metapath.EQNameUtils.IEQNamePrefixResolver; import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import javax.xml.XMLConstants; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -44,6 +49,8 @@ public final class StaticContext { @NonNull private static final Map WELL_KNOWN_NAMESPACES; + @NonNull + private static final Map WELL_KNOWN_URI_TO_PREFIX; static { Map knownNamespaces = new ConcurrentHashMap<>(); @@ -59,13 +66,29 @@ public final class StaticContext { knownNamespaces.put( MetapathConstants.PREFIX_XPATH_FUNCTIONS_MATH, MetapathConstants.NS_METAPATH_FUNCTIONS_MATH); + 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); + + WELL_KNOWN_URI_TO_PREFIX = ObjectUtils.notNull(WELL_KNOWN_NAMESPACES.entrySet().stream() + .collect(Collectors.toUnmodifiableMap( + entry -> entry.getValue().toASCIIString(), + Map.Entry::getKey, + (v1, v2) -> v2))); } @Nullable private final URI baseUri; @NonNull private final Map knownNamespaces; + @Nullable + private final URI defaultModelNamespace; + @Nullable + private final URI defaultFunctionNamespace; /** * Get the mapping of prefix to namespace URI for all well-known namespaces @@ -77,36 +100,49 @@ public final class StaticContext { * @return the mapping of prefix to namespace URI for all well-known namespaces */ @SuppressFBWarnings("MS_EXPOSE_REP") - public static Map getWellKnownNamespaces() { + public static Map getWellKnownNamespacesMap() { return WELL_KNOWN_NAMESPACES; } /** - * Create a new static context instance using default values. + * Get the mapping of namespace URIs to prefixes for all well-known namespaces + * provided by default to the static context. * - * @return a new static context instance + * @return the mapping of namespace URI to prefix for all well-known namespaces */ - @NonNull - public static StaticContext instance() { - return builder().build(); + @SuppressFBWarnings("MS_EXPOSE_REP") + public static Map getWellKnownURIToPrefixMap() { + return WELL_KNOWN_URI_TO_PREFIX; } /** - * Create a new static context builder that allows for fine-grained adjustments - * when creating a new static context. + * Get the namespace prefix associated with the provided URI, if the URI is + * well-known. * - * @return a new builder + * @param uri + * the URI to get the prefix for + * @return the prefix or {@code null} if the provided URI is not well-known + */ + @Nullable + public static String getWellKnownPrefixForUri(@NonNull String uri) { + return WELL_KNOWN_URI_TO_PREFIX.get(uri); + } + + /** + * Create a new static context instance using default values. + * + * @return a new static context instance */ @NonNull - public static Builder builder() { - return new Builder(); + public static StaticContext instance() { + return builder().build(); } - private StaticContext( - @Nullable URI baseUri, - @NonNull Map knownNamespaces) { - this.baseUri = baseUri; - this.knownNamespaces = knownNamespaces; + private StaticContext(Builder builder) { + this.baseUri = builder.baseUri; + this.knownNamespaces = CollectionUtil.unmodifiableMap(ObjectUtils.notNull(Map.copyOf(builder.namespaces))); + this.defaultModelNamespace = builder.defaultModelNamespace; + this.defaultFunctionNamespace = builder.defaultFunctionNamespace; } /** @@ -132,14 +168,14 @@ public URI getBaseUri() { * namespace bindings when a prefix match is not found. *

* The well-known namespace bindings can be retrieved using the - * {@link StaticContext#getWellKnownNamespaces()} method. + * {@link StaticContext#getWellKnownNamespacesMap()} method. * * @param prefix * the namespace prefix * @return the namespace URI bound to the prefix, or {@code null} if no * namespace is bound to the prefix * @see Builder#namespace(String, URI) - * @see #getWellKnownNamespaces() + * @see #getWellKnownNamespacesMap() */ @Nullable public URI lookupNamespaceURIForPrefix(@NonNull String prefix) { @@ -167,22 +203,182 @@ public String lookupNamespaceForPrefix(@NonNull String prefix) { } /** - * Generate a new dynamic context. + * Get the default namespace for assembly, field, or flag references that have + * no namespace prefix. + * + * @return the namespace if defined or {@code null} otherwise + */ + @Nullable + public URI getDefaultModelNamespace() { + return defaultModelNamespace; + } + + /** + * Get the default namespace for function references that have no namespace + * prefix. + * + * @return the namespace if defined or {@code null} otherwise + */ + @Nullable + public URI getDefaultFunctionNamespace() { + return defaultFunctionNamespace; + } + + /** + * Get a prefix resolver for use with Metapath function names that will attempt + * to identify the namespace corresponding to a given prefix. + *

+ * This will use the following lookup order, advancing to the next when a + * {@code null} value is returned: + *

    + *
  1. Lookup the prefix using + * {@link StaticContext#lookupNamespaceForPrefix(String)}
  2. + *
  3. Return the result of + * {@link StaticContext#getDefaultFunctionNamespace()}
  4. + *
  5. Return {@link XMLConstants#NULL_NS_URI}
  6. + *
+ * + * @return the resolver + */ + @NonNull + public IEQNamePrefixResolver getFunctionPrefixResolver() { + return this::resolveFunctionPrefix; + } + + @NonNull + private String resolveFunctionPrefix(@NonNull String prefix) { + String ns = lookupNamespaceForPrefix(prefix); + if (ns == null) { + URI uri = getDefaultFunctionNamespace(); + if (uri != null) { + ns = uri.toASCIIString(); + } + } + return ns == null ? XMLConstants.NULL_NS_URI : ns; + } + + /** + * Get a prefix resolver for use with Metapath flag node names that will attempt + * to identify the namespace corresponding to a given prefix. + *

+ * This will use the following lookup order, advancing to the next when a + * {@code null} value is returned: + *

    + *
  1. Lookup the prefix using + * {@link StaticContext#lookupNamespaceForPrefix(String)}
  2. + *
  3. Return {@link XMLConstants#NULL_NS_URI}
  4. + *
+ * + * @return the resolver + */ + @NonNull + public IEQNamePrefixResolver getFlagPrefixResolver() { + return this::resolveFlagReferencePrefix; + } + + @NonNull + private String resolveFlagReferencePrefix(@NonNull String prefix) { + String ns = lookupNamespaceForPrefix(prefix); + return ns == null ? XMLConstants.NULL_NS_URI : ns; + } + + /** + * Get a prefix resolver for use with Metapath model node names that will + * attempt to identify the namespace corresponding to a given prefix. + *

+ * This will use the following lookup order, advancing to the next when a + * {@code null} value is returned: + *

    + *
  1. Lookup the prefix using + * {@link StaticContext#lookupNamespaceForPrefix(String)}
  2. + *
  3. Return the result of + * {@link StaticContext#getDefaultModelNamespace()}
  4. + *
  5. Return {@link XMLConstants#NULL_NS_URI}
  6. + *
+ * + * @return the resolver + */ + @NonNull + public IEQNamePrefixResolver getModelPrefixResolver() { + return this::resolveModelReferencePrefix; + } + + @NonNull + private String resolveModelReferencePrefix(@NonNull String prefix) { + String ns = lookupNamespaceForPrefix(prefix); + if (ns == null) { + URI uri = getDefaultModelNamespace(); + if (uri != null) { + ns = uri.toASCIIString(); + } + } + return ns == null ? XMLConstants.NULL_NS_URI : ns; + } + + /** + * Get a prefix resolver for use with Metapath variable names that will attempt + * to identify the namespace corresponding to a given prefix. + *

+ * This will use the following lookup order, advancing to the next when a + * {@code null} value is returned: + *

    + *
  1. Lookup the prefix using + * {@link StaticContext#lookupNamespaceForPrefix(String)}
  2. + *
  3. Return {@link XMLConstants#NULL_NS_URI}
  4. + *
* - * @return the generated dynamic context + * @return the resolver */ @NonNull - public DynamicContext dynamicContext() { - return new DynamicContext(this); + public IEQNamePrefixResolver getVariablePrefixResolver() { + return this::resolveVariablePrefix; + } + + @NonNull + private String resolveVariablePrefix(@NonNull String prefix) { + String ns = lookupNamespaceForPrefix(prefix); + return ns == null ? XMLConstants.NULL_NS_URI : ns; + } + + /** + * Get a new static context builder that is pre-populated with the setting of + * this static context. + * + * @return a new builder + */ + @NonNull + public Builder buildFrom() { + Builder builder = builder(); + builder.baseUri = this.baseUri; + builder.namespaces.putAll(this.knownNamespaces); + builder.defaultModelNamespace = this.defaultModelNamespace; + builder.defaultFunctionNamespace = this.defaultFunctionNamespace; + return builder; + } + + /** + * Create a new static context builder that allows for fine-grained adjustments + * when creating a new static context. + * + * @return a new builder + */ + @NonNull + public static Builder builder() { + return new Builder(); } /** * A builder used to generate the static context. */ public static final class Builder { + @Nullable private URI baseUri; @NonNull private final Map namespaces = new ConcurrentHashMap<>(); + @Nullable + private URI defaultModelNamespace; + @Nullable + private URI defaultFunctionNamespace = MetapathConstants.NS_METAPATH_FUNCTIONS; private Builder() { namespaces.put( @@ -223,7 +419,7 @@ public Builder baseUri(@NonNull URI uri) { * {@link StaticContext#lookupNamespaceForPrefix(String)} method. *

* Well-known namespace bindings are used by default, which can be retrieved - * using the {@link StaticContext#getWellKnownNamespaces()} method. + * using the {@link StaticContext#getWellKnownNamespacesMap()} method. * * @param prefix * the prefix to associate with the namespace, which may be @@ -232,7 +428,7 @@ public Builder baseUri(@NonNull URI uri) { * @return this builder * @see StaticContext#lookupNamespaceForPrefix(String) * @see StaticContext#lookupNamespaceURIForPrefix(String) - * @see StaticContext#getWellKnownNamespaces() + * @see StaticContext#getWellKnownNamespacesMap() */ @NonNull public Builder namespace(@NonNull String prefix, @NonNull URI uri) { @@ -240,6 +436,85 @@ public Builder namespace(@NonNull String prefix, @NonNull URI uri) { return this; } + /** + * A convenience method for {@link #namespace(String, URI)}. + * + * @param prefix + * the prefix to associate with the namespace, which may be + * @param uri + * the namespace URI + * @return this builder + * @throws IllegalArgumentException + * if the provided URI is invalid + * @see StaticContext#lookupNamespaceForPrefix(String) + * @see StaticContext#lookupNamespaceURIForPrefix(String) + * @see StaticContext#getWellKnownNamespacesMap() + */ + @NonNull + public Builder namespace(@NonNull String prefix, @NonNull String uri) { + return namespace(prefix, ObjectUtils.notNull(URI.create(uri))); + } + + /** + * Defines the default namespace to use for assembly, field, or flag references + * that have no namespace prefix. + * + * @param uri + * the namespace URI + * @return this builder + * @see StaticContext#getDefaultModelNamespace() + */ + @NonNull + public Builder defaultModelNamespace(@NonNull URI uri) { + this.defaultModelNamespace = uri; + return this; + } + + /** + * A convenience method for {@link #defaultModelNamespace(URI)}. + * + * @param uri + * the namespace URI + * @return this builder + * @throws IllegalArgumentException + * if the provided URI is invalid + * @see StaticContext#getDefaultModelNamespace() + */ + @NonNull + public Builder defaultModelNamespace(@NonNull String uri) { + return defaultModelNamespace(ObjectUtils.notNull(URI.create(uri))); + } + + /** + * Defines the default namespace to use for assembly, field, or flag references + * that have no namespace prefix. + * + * @param uri + * the namespace URI + * @return this builder + * @see StaticContext#getDefaultFunctionNamespace() + */ + @NonNull + public Builder defaultFunctionNamespace(@NonNull URI uri) { + this.defaultFunctionNamespace = uri; + return this; + } + + /** + * A convenience method for {@link #defaultFunctionNamespace(URI)}. + * + * @param uri + * the namespace URI + * @return this builder + * @throws IllegalArgumentException + * if the provided URI is invalid + * @see StaticContext#getDefaultFunctionNamespace() + */ + @NonNull + public Builder defaultFunctionNamespace(@NonNull String uri) { + return defaultFunctionNamespace(ObjectUtils.notNull(URI.create(uri))); + } + /** * Construct a new static context using the information provided to the builder. * @@ -247,9 +522,7 @@ public Builder namespace(@NonNull String prefix, @NonNull URI uri) { */ @NonNull public StaticContext build() { - return new StaticContext( - baseUri, - CollectionUtil.unmodifiableMap(namespaces)); + return new StaticContext(this); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java index eea69c7a7..0331a3cff 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StaticMetapathException.java @@ -129,7 +129,7 @@ public StaticMetapathException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "MPST"; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/TypeMetapathException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/TypeMetapathException.java index 89de22791..9066b5976 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/TypeMetapathException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/TypeMetapathException.java @@ -106,7 +106,7 @@ public TypeMetapathException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "MPTY"; } } 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 3b3d16983..1cd5daa56 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 @@ -32,11 +32,14 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AndexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ArgumentContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ArgumentlistContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ArrayconstructorContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ArrowexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ArrowfunctionspecifierContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AxisstepContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ComparisonexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ContextitemexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.CurlyarrayconstructorContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.EnclosedexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.EqnameContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ExprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ExprsingleContext; @@ -47,8 +50,14 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.GeneralcompContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.IfexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.IntersectexceptexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.KeyspecifierContext; 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; @@ -71,9 +80,11 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimpleletbindingContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimpleletclauseContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimplemapexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SquarearrayconstructorContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.StepexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.StringconcatexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnaryexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnarylookupContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnionexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ValuecompContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ValueexprContext; @@ -138,10 +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) { @@ -169,10 +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) { @@ -180,10 +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. @@ -215,10 +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. @@ -241,10 +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. @@ -262,10 +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. @@ -282,10 +290,9 @@ public R visitContextitemexpr(ContextitemexprContext ctx) { return handle(ctx, (context) -> handleContextitemexpr(ctx)); } - /* ========================================================================= - * Static Function Calls - https://www.w3.org/TR/xpath-31/#id-function-calls - * ========================================================================= - */ + // ========================================================================= + // Static Function Calls - https://www.w3.org/TR/xpath-31/#id-function-calls + // ========================================================================= /** * Handle the provided expression. @@ -314,10 +321,19 @@ public R visitArgument(ArgumentContext ctx) { throw new IllegalStateException(); } - /* ========================================================================= - * Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression - * ========================================================================= - */ + // ======================================================================= + // Enclosed Expressions - https://www.w3.org/TR/xpath-31/#id-enclosed-expr + // ======================================================================= + + @Override + public R visitEnclosedexpr(EnclosedexprContext ctx) { + ExprContext expr = ctx.expr(); + return expr == null ? null : expr.accept(this); + } + + // ========================================================================= + // Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression + // ========================================================================= /** * Handle the provided expression. @@ -334,16 +350,39 @@ public R visitPostfixexpr(PostfixexprContext ctx) { return handle(ctx, (context) -> handlePostfixexpr(ctx)); } + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result + */ + protected abstract R handlePredicate(@NonNull PredicateContext ctx); + @Override public R visitPredicate(PredicateContext ctx) { - // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + assert ctx != null; + return handlePredicate(ctx); } - /* ====================================================================== - * Path Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions - * ====================================================================== + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result */ + protected abstract R handleLookup(@NonNull LookupContext ctx); + + @Override + public R visitLookup(LookupContext ctx) { + assert ctx != null; + return handleLookup(ctx); + } + + // ====================================================================== + // Path Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions + // ====================================================================== /** * Handle the provided expression. @@ -360,10 +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. @@ -380,10 +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) { @@ -423,10 +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. @@ -449,10 +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) { @@ -466,37 +502,26 @@ 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) { - // TODO: revisit once kindtest is implemented - assert ctx != null; - return delegateToChild(ctx); + // should never be called, since this is handled by the calling context + throw new IllegalStateException(); } @Override public R visitNametest(NametestContext ctx) { - assert ctx != null; - return delegateToChild(ctx); + // should never be called, since this is handled by the calling context + throw new IllegalStateException(); } - /** - * Handle the provided expression. - * - * @param ctx - * the provided expression context - * @return the result - */ - protected abstract R handleEqname(@NonNull EqnameContext ctx); - @Override public R visitEqname(EqnameContext ctx) { - assert ctx != null; - return handleEqname(ctx); + // should never be called, since this is handled by the calling context + throw new IllegalStateException(); } /** @@ -506,6 +531,7 @@ public R visitEqname(EqnameContext ctx) { * the provided expression context * @return the result */ + @NonNull protected abstract R handleWildcard(@NonNull WildcardContext ctx); @Override @@ -514,10 +540,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. @@ -549,10 +574,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. @@ -569,10 +593,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. @@ -604,10 +627,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. @@ -660,10 +682,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. @@ -680,10 +702,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. @@ -712,10 +733,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. @@ -747,10 +767,9 @@ public R visitAndexpr(AndexprContext ctx) { return handle(ctx, (context) -> handleAndexpr(ctx)); } - /* ==================================================================== - * For Expressions - https://www.w3.org/TR/xpath-31/#id-for-expressions - * ==================================================================== - */ + // ==================================================================== + // For Expressions - https://www.w3.org/TR/xpath-31/#id-for-expressions + // ==================================================================== /** * Handle the provided expression. @@ -779,10 +798,9 @@ public R visitSimpleforbinding(SimpleforbindingContext ctx) { throw new IllegalStateException(); } - /* ==================================================================== - * Let Expressions - https://www.w3.org/TR/xpath-31/#id-let-expressions - * ==================================================================== - */ + // ==================================================================== + // Let Expressions - https://www.w3.org/TR/xpath-31/#id-let-expressions + // ==================================================================== /** * Handle the provided expression. @@ -811,10 +829,107 @@ public R visitSimpleletbinding(SimpleletbindingContext ctx) { throw new IllegalStateException(); } - /* ========================================================================= - * Conditional Expressions - https://www.w3.org/TR/xpath-31/#id-conditionals - * ========================================================================= + // ====================================================================== + // 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 + // ============================================================== + + @Override + public R visitArrayconstructor(ArrayconstructorContext ctx) { + assert ctx != null; + return delegateToChild(ctx); + } + + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result + */ + protected abstract R handleArrayConstructor(@NonNull SquarearrayconstructorContext ctx); + + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result + */ + protected abstract R handleArrayConstructor(@NonNull CurlyarrayconstructorContext ctx); + + @Override + public R visitSquarearrayconstructor(SquarearrayconstructorContext ctx) { + assert ctx != null; + return handleArrayConstructor(ctx); + } + + @Override + public R visitCurlyarrayconstructor(CurlyarrayconstructorContext ctx) { + assert ctx != null; + return handleArrayConstructor(ctx); + } + + @Override + public R visitKeyspecifier(KeyspecifierContext ctx) { + // should never be called, since this is handled by the parent expression + throw new IllegalStateException(); + } + + /** + * Handle the provided expression. + * + * @param ctx + * the provided expression context + * @return the result */ + protected abstract R handleUnarylookup(@NonNull UnarylookupContext ctx); + + @Override + public R visitUnarylookup(UnarylookupContext ctx) { + assert ctx != null; + return handleUnarylookup(ctx); + } + + // ========================================================================= + // Conditional Expressions - https://www.w3.org/TR/xpath-31/#id-conditionals + // ========================================================================= /** * Handle the provided expression. @@ -831,9 +946,12 @@ public R visitIfexpr(IfexprContext ctx) { return handle(ctx, (context) -> handleIfexpr(ctx)); } - /* ================================================================================== - * Quantified Expressions - https://www.w3.org/TR/xpath-31/#id-quantified-expressions - * ================================================================================== + /* + * ============================================================================= + * ===== Quantified Expressions - + * https://www.w3.org/TR/xpath-31/#id-quantified-expressions + * ============================================================================= + * ===== */ /** @@ -851,7 +969,8 @@ public R visitQuantifiedexpr(QuantifiedexprContext ctx) { return handleQuantifiedexpr(ctx); } - /* ========================================================================= + /* + * ========================================================================= * Simple map operator (!) - https://www.w3.org/TR/xpath-31/#id-map-operator * ========================================================================= */ @@ -871,8 +990,9 @@ public R visitSimplemapexpr(SimplemapexprContext ctx) { return handle(ctx, (context) -> handleSimplemapexpr(ctx)); } - /* ======================================================================= - * Arrow operator (=>) - https://www.w3.org/TR/xpath-31/#id-arrow-operator + /* + * ======================================================================= Arrow + * operator (=>) - https://www.w3.org/TR/xpath-31/#id-arrow-operator * ======================================================================= */ 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 637d24caf..44f7fc102 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 @@ -47,7 +47,11 @@ import javax.xml.namespace.QName; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +@SuppressWarnings({ + "PMD.CouplingBetweenObjects" +}) public abstract class AbstractCSTVisitorBase extends AbstractAstVisitor { @@ -130,6 +134,49 @@ public IExpression visit(ParseTree tree) { return super.visit(tree); } + /** + * Parse the provided context as an n-ary phrase. + * + * @param + * the Java type of the antlr context to parse + * @param + * the Java type of the child expressions produced by this parser + * @param + * the Java type of the outer expression produced by the parser + * @param context + * the antlr context to parse + * @param startIndex + * the child index to start parsing on + * @param step + * the increment to advance while parsing child expressions + * @param parser + * a binary function used to produce child expressions + * @param supplier + * a function used to produce the other expression + * @return the outer expression or {@code null} if no children exist to parse + */ + @Nullable + protected + R nairyToCollection( + @NonNull CONTEXT context, + int startIndex, + int step, + @NonNull BiFunction parser, + @NonNull Function, R> supplier) { + int numChildren = context.getChildCount(); + + R retval = null; + if (startIndex < numChildren) { + List children = new ArrayList<>((numChildren - startIndex) / step); + for (int idx = startIndex; idx < numChildren; idx += step) { + T result = parser.apply(context, idx); + children.add(result); + } + retval = supplier.apply(children); + } + return retval; + } + /** * Parse the provided context as an n-ary phrase, which will be one of the * following. @@ -272,12 +319,11 @@ protected IExpression handleGroupedNAiry( IExpression retval = null; if (numChildren > 0) { ParseTree leftTree = context.getChild(startingIndex); - IExpression result = ObjectUtils.notNull(leftTree.accept(this)); + retval = ObjectUtils.notNull(leftTree.accept(this)); for (int i = startingIndex + 1; i < numChildren; i = i + step) { - result = parser.apply(context, i, result); + retval = parser.apply(context, i, retval); } - retval = result; } return retval; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java index 967b413c2..b60654f2f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractExpression.java @@ -28,9 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.TypeMetapathException; -import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; 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 edu.umd.cs.findbugs.annotations.NonNull; @@ -54,9 +52,7 @@ public abstract class AbstractExpression implements IExpression { @Nullable public static IAnyAtomicItem getFirstDataItem(@NonNull ISequence sequence, boolean requireSingleton) { - IItem item = FunctionUtils.getFirstItem(sequence, requireSingleton); - - return item == null ? null : FnData.fnDataItem(item); + return FnData.fnData(sequence).getFirstItem(requireSingleton); } @Override 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 24e5fe06e..d83c8b90b 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 @@ -35,14 +35,17 @@ import gov.nist.secauto.metaschema.core.metapath.cst.math.Multiplication; import gov.nist.secauto.metaschema.core.metapath.cst.math.Subtraction; import gov.nist.secauto.metaschema.core.metapath.cst.path.Axis; +import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem; import gov.nist.secauto.metaschema.core.metapath.cst.path.Flag; import gov.nist.secauto.metaschema.core.metapath.cst.path.ModelInstance; +import gov.nist.secauto.metaschema.core.metapath.cst.path.NameTest; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashOnlyPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.Step; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Wildcard; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -56,7 +59,7 @@ * @param * additional state to pass between nodes visited */ -@SuppressWarnings("PMD.CouplingBetweenObjects") +@SuppressWarnings({ "PMD.CouplingBetweenObjects", "PMD.ExcessivePublicCount" }) public abstract class AbstractExpressionVisitor implements IExpressionVisitor { /** @@ -202,7 +205,7 @@ public RESULT visitFlag(Flag expr, CONTEXT context) { } @Override - public RESULT visitFunctionCall(FunctionCall expr, CONTEXT context) { + public RESULT visitFunctionCall(StaticFunctionCall expr, CONTEXT context) { return visitChildren(expr, context); } @@ -242,7 +245,7 @@ public RESULT visitMultiplication(Multiplication expr, CONTEXT context) { } @Override - public RESULT visitName(Name expr, CONTEXT context) { + public RESULT visitName(NameTest expr, CONTEXT context) { return defaultResult(); } @@ -257,7 +260,7 @@ public RESULT visitOr(Or expr, CONTEXT context) { } @Override - public RESULT visitPredicate(Predicate expr, CONTEXT context) { + public RESULT visitPredicate(PredicateExpression expr, CONTEXT context) { return visitChildren(expr, context); } @@ -350,4 +353,39 @@ public RESULT visitFor(For expr, CONTEXT context) { public RESULT visitSimpleMap(SimpleMap expr, CONTEXT context) { return visitChildren(expr, context); } + + @Override + public RESULT visitMapConstructor(MapConstructor expr, CONTEXT context) { + return visitChildren(expr, context); + } + + @Override + 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); + } + + @Override + public RESULT visitPostfixLookup(PostfixLookup expr, CONTEXT context) { + return visitChildren(expr, context); + } + + @Override + public RESULT visitFunctionCallAccessor(FunctionCallAccessor expr, CONTEXT context) { + return visitChildren(expr, context); + } + + @Override + public RESULT visitUnaryLookup(UnaryLookup expr, CONTEXT context) { + return defaultResult(); + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractFilterExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractFilterExpression.java index 0408ab3ed..5b20b87ff 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractFilterExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractFilterExpression.java @@ -71,7 +71,7 @@ public ISequence accept( ISequence left = getLeft().accept(dynamicContext, focus); ISequence right = getRight().accept(dynamicContext, focus); - List rightList = right.asList(); + List rightList = right.getValue(); return applyFilterTo(left, rightList); } 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 new file mode 100644 index 000000000..752b242f8 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractLookup.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.cst; + +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * An implementation of + * Lookup Operators + * supporting access to items in Metapath maps and arrays. + *

+ * Provides support for various types of key- and index-based lookups related to + * {@link IMapItem} and {@link IArrayItem} objects. + */ +public abstract class AbstractLookup implements IExpression { + @NonNull + private final IKeySpecifier keySpecifier; + + /** + * Construct a new lookup expression that uses the provided key specifier. + * + * @param keySpecifier + * the key specifier that identifies how to lookup entries + */ + protected AbstractLookup(@NonNull IKeySpecifier keySpecifier) { + this.keySpecifier = keySpecifier; + } + + /** + * Get the key specifier implementation. + * + * @return the key specifier + */ + @NonNull + public IKeySpecifier getKeySpecifier() { + return keySpecifier; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractNamedInstanceExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractNamedInstanceExpression.java index 69d79049f..1c00129d1 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractNamedInstanceExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/AbstractNamedInstanceExpression.java @@ -27,6 +27,8 @@ package gov.nist.secauto.metaschema.core.metapath.cst; import gov.nist.secauto.metaschema.core.metapath.cst.path.AbstractPathExpression; +import gov.nist.secauto.metaschema.core.metapath.cst.path.NameTest; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Wildcard; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import java.util.List; @@ -50,7 +52,7 @@ public AbstractNamedInstanceExpression(@NonNull IExpression test) { } /** - * Get the {@link Wildcard} or {@link Name} test. + * Get the {@link Wildcard} or {@link NameTest} test. * * @return the test */ diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java new file mode 100644 index 000000000..f80aa6979 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySequenceConstructor.java @@ -0,0 +1,91 @@ +/* + * 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.ISequence; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * An implementation of the + * Array Curly + * Constructor supporting the creation of a Metapath {@link IArrayItem}. + */ +public class ArraySequenceConstructor implements IExpression { + @Nullable + private final IExpression expr; + + /** + * Construct a new array constructor expression that uses the provided + * expression to initialize the array. + * + * @param expr + * the expression used to produce the array members + */ + public ArraySequenceConstructor(@Nullable IExpression expr) { + this.expr = expr; + } + + @SuppressWarnings("rawtypes") + @Override + public Class getBaseResultType() { + return IArrayItem.class; + } + + @SuppressWarnings("rawtypes") + @Override + public Class getStaticResultType() { + return IArrayItem.class; + } + + @SuppressWarnings("null") + @Override + public List getChildren() { + return List.of(expr); + } + + @Override + public ISequence> accept(DynamicContext dynamicContext, ISequence focus) { + ISequence> retval; + if (expr != null) { + IArrayItem array = IArrayItem.ofCollection(expr.accept(dynamicContext, focus)); + retval = ISequence.of(array); + } else { + retval = ISequence.of(); + } + return retval; + } + + @Override + 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/Wildcard.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java similarity index 62% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Wildcard.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java index 087e7cdf5..f670e2e54 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Wildcard.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ArraySquareConstructor.java @@ -28,43 +28,51 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ISequence; -import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; -import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; -import java.util.Collections; import java.util.List; +import edu.umd.cs.findbugs.annotations.NonNull; + /** - * The CST node for a Metapath - * wildcard name - * test. + * An implementation of the + * Array Square + * Constructor supporting the creation of a Metapath {@link IArrayItem}. */ -public class Wildcard implements IExpression { - @SuppressWarnings("null") - @Override - public List getChildren() { - return Collections.emptyList(); - } +public class ArraySquareConstructor implements IExpression { + @NonNull + private final List children; - @Override - public Class getBaseResultType() { - return INodeItem.class; + /** + * Construct a new array constructor expression that uses the provided + * expression to initialize the array. + *

+ * Each resulting array member contains the value of the corresponding argument + * expression. + * + * @param children + * the expressions used to produce the array members + */ + public ArraySquareConstructor(@NonNull List children) { + this.children = children; } @Override - public Class getStaticResultType() { - return getBaseResultType(); + public List getChildren() { + return children; } @Override - public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { - return visitor.visitWildcard(this, context); + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + return ISequence.of(getChildren().stream() + .map(expr -> expr.accept(dynamicContext, focus)) + .map(ISequence::toCollectionValue) + .collect(IArrayItem.toArrayItem())); } @Override - public ISequence accept( - DynamicContext dynamicContext, ISequence focus) { - return ISequence.of(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item))); + 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 157a3875a..f2f407b05 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 @@ -26,6 +26,8 @@ package gov.nist.secauto.metaschema.core.metapath.cst; +import gov.nist.secauto.metaschema.core.metapath.EQNameUtils; +import gov.nist.secauto.metaschema.core.metapath.StaticContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AbbrevforwardstepContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AbbrevreversestepContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AdditiveexprContext; @@ -36,6 +38,7 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.AxisstepContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ComparisonexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ContextitemexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.CurlyarrayconstructorContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.EqnameContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ExprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ExprsingleContext; @@ -45,9 +48,15 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.GeneralcompContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.IfexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.IntersectexceptexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.KeyspecifierContext; 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; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.NumericliteralContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.OrexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ParenthesizedexprContext; @@ -63,8 +72,10 @@ import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimpleletbindingContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimpleletclauseContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SimplemapexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.SquarearrayconstructorContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.StringconcatexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnaryexprContext; +import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnarylookupContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.UnionexprContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.ValuecompContext; import gov.nist.secauto.metaschema.core.metapath.antlr.Metapath10.VarnameContext; @@ -80,15 +91,24 @@ import gov.nist.secauto.metaschema.core.metapath.cst.math.Multiplication; import gov.nist.secauto.metaschema.core.metapath.cst.math.Subtraction; import gov.nist.secauto.metaschema.core.metapath.cst.path.Axis; +import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem; import gov.nist.secauto.metaschema.core.metapath.cst.path.Flag; +import gov.nist.secauto.metaschema.core.metapath.cst.path.INameTestExpression; +import gov.nist.secauto.metaschema.core.metapath.cst.path.INodeTestExpression; import gov.nist.secauto.metaschema.core.metapath.cst.path.ModelInstance; +import gov.nist.secauto.metaschema.core.metapath.cst.path.NameTest; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashOnlyPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.Step; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Wildcard; import gov.nist.secauto.metaschema.core.metapath.function.ComparisonFunctions; +import gov.nist.secauto.metaschema.core.metapath.impl.AbstractKeySpecifier; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -103,9 +123,12 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -119,11 +142,32 @@ }) public class BuildCSTVisitor extends AbstractCSTVisitorBase { + @NonNull + private final StaticContext context; + + /** + * Construct a new compact syntax tree generating visitor. + * + * @param context + * the static Metapath evaluation context + */ + public BuildCSTVisitor(@NonNull StaticContext context) { + this.context = context; + } - /* ============================================================ - * Expressions - https://www.w3.org/TR/xpath-31/#id-expressions - * ============================================================ + // ============================================================ + // Expressions - https://www.w3.org/TR/xpath-31/#id-expressions + // ============================================================ + + /** + * Get the static Metapath evaluation context. + * + * @return the context */ + @NonNull + protected StaticContext getContext() { + return context; + } @Override protected IExpression handleExpr(ExprContext ctx) { @@ -133,10 +177,9 @@ protected IExpression handleExpr(ExprContext ctx) { }); } - /* ================================================================= - * Literal Expressions - https://www.w3.org/TR/xpath-31/#id-literals - * ================================================================= - */ + // ================================================================= + // Literal Expressions - https://www.w3.org/TR/xpath-31/#id-literals + // ================================================================= @Override protected IExpression handleStringLiteral(LiteralContext ctx) { @@ -163,42 +206,252 @@ protected IExpression handleNumericLiteral(NumericliteralContext ctx) { return retval; } - /* ================================================================== - * Variable References - https://www.w3.org/TR/xpath-31/#id-variables - * ================================================================== - */ + // ================================================================== + // Variable References - https://www.w3.org/TR/xpath-31/#id-variables + // ================================================================== @Override protected IExpression handleVarref(VarrefContext ctx) { - Name varName = (Name) ctx.varname().accept(this); - assert varName != null; - return new VariableReference(varName); + return new VariableReference( + EQNameUtils.parseName( + ObjectUtils.notNull(ctx.varname().eqname().getText()), + getContext().getVariablePrefixResolver())); } - /* ================================================================================= - * Parenthesized Expressions - https://www.w3.org/TR/xpath-31/#id-paren-expressions - * ================================================================================= - */ + // ==================================================================== + // For Expressions - https://www.w3.org/TR/xpath-31/#id-for-expressions + // ==================================================================== + + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + @Override + protected IExpression handleForexpr(ForexprContext ctx) { + SimpleforclauseContext simpleForClause = ctx.simpleforclause(); + + // for SimpleForBinding ("," SimpleForBinding)* + int bindingCount = simpleForClause.getChildCount() / 2; + + @NonNull IExpression retval = ObjectUtils.notNull(ctx.exprsingle().accept(this)); + + // step through in reverse + for (int idx = bindingCount - 1; idx >= 0; idx--) { + SimpleforbindingContext simpleForBinding = simpleForClause.simpleforbinding(idx); + + VarnameContext varName = simpleForBinding.varname(); + ExprsingleContext exprSingle = simpleForBinding.exprsingle(); + + IExpression boundExpression = exprSingle.accept(this); + assert boundExpression != null; + + QName qname = EQNameUtils.parseName( + ObjectUtils.notNull(varName.eqname().getText()), + getContext().getVariablePrefixResolver()); + + Let.VariableDeclaration variable = new Let.VariableDeclaration(qname, boundExpression); + + retval = new For(variable, retval); + } + return retval; + } + + // ==================================================================== + // Let Expressions - https://www.w3.org/TR/xpath-31/#id-let-expressions + // ==================================================================== + + @Override + protected IExpression handleLet(LetexprContext context) { + @NonNull IExpression retval = ObjectUtils.notNull(context.exprsingle().accept(this)); + + SimpleletclauseContext letClause = context.simpleletclause(); + List clauses = letClause.simpleletbinding(); + + ListIterator reverseListIterator = clauses.listIterator(clauses.size()); + while (reverseListIterator.hasPrevious()) { + SimpleletbindingContext simpleCtx = reverseListIterator.previous(); + + IExpression boundExpression = simpleCtx.exprsingle().accept(this); + assert boundExpression != null; + + QName varName = EQNameUtils.parseName( + ObjectUtils.notNull(simpleCtx.varname().eqname().getText()), + getContext().getVariablePrefixResolver()); + + retval = new Let(varName, boundExpression, retval); // NOPMD intended + } + return retval; + } + + // ====================================================================== + // Map Constructors - https://www.w3.org/TR/xpath-31/#id-map-constructors + // ====================================================================== + + @Override + protected MapConstructor handleMapConstructor(MapconstructorContext context) { + return context.getChildCount() == 3 + // 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( + ObjectUtils.notNull(entry.mapkeyexpr().accept(this)), + ObjectUtils.notNull(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) { + 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 ArraySequenceConstructor(visit(ctx.enclosedexpr())); + } + + // =============================================== + // Unary Lookup - + // https://www.w3.org/TR/xpath-31/#id-unary-lookup + // =============================================== + + @Override + protected IExpression handleUnarylookup(UnarylookupContext ctx) { + KeyspecifierContext specifier = ctx.keyspecifier(); + + IKeySpecifier keySpecifier; + if (specifier.parenthesizedexpr() != null) { + keySpecifier = AbstractKeySpecifier.newParenthesizedExprKeySpecifier( + ObjectUtils.requireNonNull(specifier.parenthesizedexpr().accept(this))); + } else if (specifier.NCName() != null) { + keySpecifier = AbstractKeySpecifier.newNameKeySpecifier( + ObjectUtils.requireNonNull(specifier.NCName().getText())); + } else if (specifier.IntegerLiteral() != null) { + keySpecifier = AbstractKeySpecifier.newIntegerLiteralKeySpecifier( + IIntegerItem.valueOf(ObjectUtils.requireNonNull(specifier.IntegerLiteral().getText()))); + } else if (specifier.STAR() != null) { + keySpecifier = AbstractKeySpecifier.newWildcardKeySpecifier(); + } else { + throw new UnsupportedOperationException("unknown key specifier"); + } + return new UnaryLookup(keySpecifier); + } + + // ========================================================= + // Quantified Expressions - + // https://www.w3.org/TR/xpath-31/#id-quantified-expressions + // ========================================================= + + @Override + protected IExpression handleQuantifiedexpr(QuantifiedexprContext ctx) { + Quantified.Quantifier quantifier; + int type = ((TerminalNode) ctx.getChild(0)).getSymbol().getType(); + switch (type) { + case Metapath10Lexer.KW_SOME: + quantifier = Quantified.Quantifier.SOME; + break; + case Metapath10Lexer.KW_EVERY: + quantifier = Quantified.Quantifier.EVERY; + break; + default: + throw new UnsupportedOperationException(((TerminalNode) ctx.getChild(0)).getSymbol().getText()); + } + + int numVars = (ctx.getChildCount() - 2) / 5; // children - "satisfies expr" / ", $ varName in expr" + Map vars = new LinkedHashMap<>(); // NOPMD ordering needed + int offset = 0; + for (; offset < numVars; offset++) { + // $ + QName varName = EQNameUtils.parseName( + ObjectUtils.notNull(ctx.varname(offset).eqname().getText()), + getContext().getVariablePrefixResolver()); + + // in + IExpression varExpr = visit(ctx.exprsingle(offset)); + + vars.put(varName, varExpr); + } + + IExpression satisfies = visit(ctx.exprsingle(offset)); + + return new Quantified(quantifier, vars, satisfies); + } + + // ======================================================================= + // Arrow operator (=>) - https://www.w3.org/TR/xpath-31/#id-arrow-operator + // ======================================================================= + + @Override + protected IExpression handleArrowexpr(ArrowexprContext context) { + // TODO: handle additional syntax for varef and parenthesized + return handleGroupedNAiry(context, 0, 3, (ctx, idx, left) -> { + // the next child is "=>" + assert "=>".equals(ctx.getChild(idx).getText()); + + int offset = (idx - 1) / 3; + + ArrowfunctionspecifierContext fcCtx = ctx.getChild(ArrowfunctionspecifierContext.class, offset); + ArgumentlistContext argumentCtx = ctx.getChild(ArgumentlistContext.class, offset); + + QName name = EQNameUtils.parseName( + ObjectUtils.notNull(fcCtx.eqname().getText()), + getContext().getFunctionPrefixResolver()); + + try (Stream args = Stream.concat( + Stream.of(left), + parseArgumentList(ObjectUtils.notNull(argumentCtx)))) { + assert args != null; + + return new StaticFunctionCall(name, ObjectUtils.notNull(args.collect(Collectors.toUnmodifiableList()))); + } + }); + } + + // ==================================================== + // Parenthesized Expressions - + // https://www.w3.org/TR/xpath-31/#id-paren-expressions + // ==================================================== @Override protected IExpression handleEmptyParenthesizedexpr(ParenthesizedexprContext ctx) { return EmptySequence.instance(); } - /* ===================================================================================== - * 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 + // ========================================================== @Override protected IExpression handleContextitemexpr(ContextitemexprContext ctx) { return ContextItem.instance(); } - /* ========================================================================= - * Static Function Calls - https://www.w3.org/TR/xpath-31/#id-function-calls - * ========================================================================= - */ + // ========================================================================= + // Static Function Calls - https://www.w3.org/TR/xpath-31/#id-function-calls + // ========================================================================= /** * Parse a list of arguments. @@ -229,21 +482,18 @@ protected Stream parseArgumentList(@NonNull ArgumentlistContext con @Override protected IExpression handleFunctioncall(FunctioncallContext ctx) { - EqnameContext nameCtx = ctx.eqname(); - String name = nameCtx.getText(); - - assert name != null; - - return new FunctionCall( - name, + QName qname = EQNameUtils.parseName( + ObjectUtils.notNull(ctx.eqname().getText()), + getContext().getFunctionPrefixResolver()); + return new StaticFunctionCall( + qname, ObjectUtils.notNull(parseArgumentList(ObjectUtils.notNull(ctx.argumentlist())) .collect(Collectors.toUnmodifiableList()))); } - /* ========================================================================= - * Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression - * ========================================================================= - */ + // ========================================================================= + // Filter Expressions - https://www.w3.org/TR/xpath-31/#id-filter-expression + // ========================================================================= /** * Parse a predicate AST. @@ -293,22 +543,66 @@ protected List parsePredicates(@NonNull ParseTree context, int star } @Override - protected IExpression handlePostfixexpr(PostfixexprContext ctx) { - int numChildren = ctx.getChildCount(); - ParseTree primaryTree = ctx.getChild(0); - IExpression retval = ObjectUtils.notNull(primaryTree.accept(this)); + protected IExpression handlePostfixexpr(PostfixexprContext context) { + return handleGroupedNAiry( + context, + 0, + 1, + (ctx, idx, left) -> { + assert left != null; + + ParseTree tree = ctx.getChild(idx); + IExpression result; + if (tree instanceof ArgumentlistContext) { + // map or array access using function call syntax + result = new FunctionCallAccessor( + left, + ObjectUtils.notNull(parseArgumentList((ArgumentlistContext) tree).findFirst().get())); + } else if (tree instanceof PredicateContext) { + result = new PredicateExpression( + left, + CollectionUtil.singletonList(parsePredicate((PredicateContext) tree))); + } else if (tree instanceof LookupContext) { + KeyspecifierContext specifier = ((LookupContext) tree).keyspecifier(); + + IKeySpecifier keySpecifier; + if (specifier.parenthesizedexpr() != null) { + keySpecifier = AbstractKeySpecifier.newParenthesizedExprKeySpecifier( + ObjectUtils.requireNonNull(specifier.parenthesizedexpr().accept(this))); + } else if (specifier.NCName() != null) { + keySpecifier = AbstractKeySpecifier.newNameKeySpecifier( + ObjectUtils.requireNonNull(specifier.NCName().getText())); + } else if (specifier.IntegerLiteral() != null) { + keySpecifier = AbstractKeySpecifier.newIntegerLiteralKeySpecifier( + IIntegerItem.valueOf(ObjectUtils.requireNonNull(specifier.IntegerLiteral().getText()))); + } else if (specifier.STAR() != null) { + keySpecifier = AbstractKeySpecifier.newWildcardKeySpecifier(); + } else { + throw new UnsupportedOperationException("unknown key specifier"); + } + result = new PostfixLookup(left, keySpecifier); + } else { + result = visit(tree); + } + return result; + }); + } - List predicates = numChildren > 1 ? parsePredicates(ctx, 1) : CollectionUtil.emptyList(); + // ====================================================================== + // Path Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions + // ====================================================================== - if (!predicates.isEmpty()) { - retval = new Predicate(retval, predicates); - } - return retval; + @Override + protected IExpression handlePredicate(PredicateContext ctx) { + parsePredicate(ctx); + return null; + } + + @Override + protected IExpression handleLookup(LookupContext ctx) { + // TODO Auto-generated method stub + return null; } - /* ====================================================================== - * Path Expressions - https://www.w3.org/TR/xpath-31/#id-path-expressions - * ====================================================================== - */ @Override protected IExpression handlePathexpr(PathexprContext ctx) { @@ -342,10 +636,10 @@ protected IExpression handlePathexpr(PathexprContext ctx) { return retval; } - /* ======================================================================================= - * 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 + // ============================================================ @Override protected IExpression handleRelativepathexpr(RelativepathexprContext context) { @@ -372,35 +666,45 @@ protected IExpression handleRelativepathexpr(RelativepathexprContext context) { }); } - /* ================================================ - * Steps - https://www.w3.org/TR/xpath-31/#id-steps - * ================================================ - */ + // ================================================ + // Steps - https://www.w3.org/TR/xpath-31/#id-steps + // ================================================ @Override protected IExpression handleForwardstep(ForwardstepContext ctx) { - assert ctx.getChildCount() == 2; + AbbrevforwardstepContext abbrev = ctx.abbrevforwardstep(); - Token token = (Token) ctx.forwardaxis().getChild(0).getPayload(); + Step retval; + if (abbrev == null) { + assert ctx.getChildCount() == 2; - Axis axis; - switch (token.getType()) { - case Metapath10Lexer.KW_SELF: - axis = Axis.SELF; - break; - case Metapath10Lexer.KW_CHILD: - axis = Axis.CHILDREN; - break; - case Metapath10Lexer.KW_DESCENDANT: - axis = Axis.DESCENDANT; - break; - case Metapath10Lexer.KW_DESCENDANT_OR_SELF: - axis = Axis.DESCENDANT_OR_SELF; - break; - default: - throw new UnsupportedOperationException(token.getText()); + Token token = (Token) ctx.forwardaxis().getChild(0).getPayload(); + + Axis axis; + switch (token.getType()) { + case Metapath10Lexer.KW_SELF: + axis = Axis.SELF; + break; + case Metapath10Lexer.KW_CHILD: + axis = Axis.CHILDREN; + break; + case Metapath10Lexer.KW_DESCENDANT: + axis = Axis.DESCENDANT; + break; + case Metapath10Lexer.KW_DESCENDANT_OR_SELF: + axis = Axis.DESCENDANT_OR_SELF; + break; + default: + throw new UnsupportedOperationException(token.getText()); + } + retval = new Step(axis, + parseNodeTest(ctx.nodetest(), false)); + } else { + retval = new Step( + Axis.CHILDREN, + parseNodeTest(ctx.nodetest(), abbrev.AT() != null)); } - return new Step(axis, visit(ctx.nametest())); + return retval; } @Override @@ -423,49 +727,97 @@ protected IExpression handleReversestep(ReversestepContext ctx) { default: throw new UnsupportedOperationException(token.getText()); } - return new Step(axis, visit(ctx.nametest())); + return new Step(axis, parseNodeTest(ctx.nodetest(), false)); } - /* ====================================================================== - * Predicates within Steps - https://www.w3.org/TR/xpath-31/#id-predicate - * ====================================================================== - */ - - @Override - protected IExpression handleAxisstep(AxisstepContext ctx) { - IExpression step = visit(ctx.getChild(0)); - ParseTree predicateTree = ctx.getChild(1); - assert predicateTree != null; - - List predicates = parsePredicates(predicateTree, 0); + // ======================================================= + // Node Tests - https://www.w3.org/TR/xpath-31/#node-tests + // ======================================================= - return predicates.isEmpty() ? step : new Predicate(step, predicates); + /** + * Parse an antlr node test expression. + * + * @param ctx + * the antrl context + * @param flag + * if the context is within a flag's scope + * @return the resulting expression + */ + @NonNull + protected INodeTestExpression parseNodeTest(NodetestContext ctx, boolean flag) { + // TODO: implement kind test + NametestContext nameTestCtx = ctx.nametest(); + return parseNameTest(nameTestCtx, flag); } - /* ======================================================= - * Node Tests - https://www.w3.org/TR/xpath-31/#node-tests - * ======================================================= + /** + * Parse an antlr name test expression. + * + * @param ctx + * the antrl context + * @param flag + * if the context is within a flag's scope + * @return the resulting expression */ + @NonNull + protected INameTestExpression parseNameTest(NametestContext ctx, boolean flag) { + ParseTree testType = ObjectUtils.requireNonNull(ctx.getChild(0)); + INameTestExpression retval; + if (testType instanceof EqnameContext) { + QName qname = EQNameUtils.parseName( + ObjectUtils.notNull(ctx.eqname().getText()), + flag ? getContext().getFlagPrefixResolver() : getContext().getModelPrefixResolver()); + retval = new NameTest(qname); + } else { // wildcard + retval = handleWildcard((WildcardContext) testType); + } + return retval; + } @Override - protected IExpression handleEqname(EqnameContext ctx) { - ParseTree tree = ctx.getChild(0); - String name = ((TerminalNode) tree).getText(); - - assert name != null; + protected Wildcard handleWildcard(WildcardContext ctx) { + Predicate> matcher = null; + if (ctx.STAR() == null) { + if (ctx.CS() != null) { + // specified prefix, any local-name + String prefix = ObjectUtils.notNull(ctx.NCName().getText()); + String namespace = getContext().lookupNamespaceForPrefix(prefix); + if (namespace == null) { + throw new IllegalStateException(String.format("Prefix '%s' did not map to a namespace.", prefix)); + } + matcher = new Wildcard.MatchAnyLocalName(namespace); + } else if (ctx.SC() != null) { + // any prefix, specified local-name + matcher = new Wildcard.MatchAnyNamespace(ObjectUtils.notNull(ctx.NCName().getText())); + } else { + // specified braced namespace, any local-name + String bracedUriLiteral = ctx.BracedURILiteral().getText(); + String namespace = ObjectUtils.notNull(bracedUriLiteral.substring(2, bracedUriLiteral.length() - 1)); + matcher = new Wildcard.MatchAnyLocalName(namespace); + } + } // star needs no matcher: any prefix, any local-name - return new Name(name); + return new Wildcard(matcher); } + // ====================================================================== + // Predicates within Steps - https://www.w3.org/TR/xpath-31/#id-predicate + // ====================================================================== + @Override - protected IExpression handleWildcard(WildcardContext ctx) { - return new Wildcard(); + protected IExpression handleAxisstep(AxisstepContext ctx) { + IExpression step = visit(ctx.getChild(0)); + ParseTree predicateTree = ctx.getChild(1); + assert predicateTree != null; + + List predicates = parsePredicates(predicateTree, 0); + + return predicates.isEmpty() ? step : new PredicateExpression(step, predicates); } - /* =========================================================== - * Abbreviated Syntax - https://www.w3.org/TR/xpath-31/#abbrev - * =========================================================== - */ + // =========================================================== + // Abbreviated Syntax - https://www.w3.org/TR/xpath-31/#abbrev + // =========================================================== @Override protected IExpression handleAbbrevforwardstep(AbbrevforwardstepContext ctx) { @@ -473,10 +825,10 @@ protected IExpression handleAbbrevforwardstep(AbbrevforwardstepContext ctx) { IExpression retval; if (numChildren == 1) { - retval = new ModelInstance(visit(ctx.getChild(0))); + retval = new ModelInstance(parseNodeTest(ctx.nodetest(), false)); } else { // this is an AT test - retval = new Flag(visit(ctx.getChild(1))); + retval = new Flag(parseNodeTest(ctx.nodetest(), true)); } return retval; } @@ -486,10 +838,9 @@ protected IExpression handleAbbrevreversestep(AbbrevreversestepContext ctx) { return Axis.PARENT; } - /* ====================================================================== - * Constructing Sequences - https://www.w3.org/TR/xpath-31/#construct_seq - * ====================================================================== - */ + // ====================================================================== + // Constructing Sequences - https://www.w3.org/TR/xpath-31/#construct_seq + // ====================================================================== @Override protected IExpression handleRangeexpr(RangeexprContext ctx) { @@ -501,10 +852,9 @@ protected IExpression handleRangeexpr(RangeexprContext ctx) { return new Range(left, right); } - /* ======================================================================== - * Combining Node Sequences - https://www.w3.org/TR/xpath-31/#combining_seq - * ======================================================================== - */ + // ======================================================================== + // Combining Node Sequences - https://www.w3.org/TR/xpath-31/#combining_seq + // ======================================================================== @Override protected IExpression handleUnionexpr(UnionexprContext ctx) { @@ -539,10 +889,9 @@ protected IExpression handleIntersectexceptexpr(IntersectexceptexprContext conte }); } - /* ====================================================================== - * Arithmetic Expressions - https://www.w3.org/TR/xpath-31/#id-arithmetic - * ====================================================================== - */ + // ====================================================================== + // Arithmetic Expressions - https://www.w3.org/TR/xpath-31/#id-arithmetic + // ====================================================================== @Override protected IExpression handleAdditiveexpr(AdditiveexprContext context) { @@ -630,10 +979,10 @@ protected IExpression handleUnaryexpr(UnaryexprContext ctx) { return retval; } - /* ======================================================================================== - * 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 + // ===================================================== @Override protected IExpression handleStringconcatexpr(StringconcatexprContext ctx) { @@ -643,10 +992,9 @@ protected IExpression handleStringconcatexpr(StringconcatexprContext ctx) { }); } - /* ======================================================================= - * Comparison Expressions - https://www.w3.org/TR/xpath-31/#id-comparisons - * ======================================================================= - */ + // ======================================================================= + // Comparison Expressions - https://www.w3.org/TR/xpath-31/#id-comparisons + // ======================================================================= @Override protected IExpression handleComparisonexpr(ComparisonexprContext ctx) { // NOPMD - ok @@ -719,10 +1067,9 @@ protected IExpression handleComparisonexpr(ComparisonexprContext ctx) { // NOPMD return retval; } - /* ============================================================================ - * Logical Expressions - https://www.w3.org/TR/xpath-31/#id-logical-expressions - * ============================================================================ - */ + // ============================================================================ + // Logical Expressions - https://www.w3.org/TR/xpath-31/#id-logical-expressions + // ============================================================================ @Override protected IExpression handleOrexpr(OrexprContext ctx) { @@ -740,72 +1087,9 @@ protected IExpression handleAndexpr(AndexprContext ctx) { }); } - /* ==================================================================== - * For Expressions - https://www.w3.org/TR/xpath-31/#id-for-expressions - * ==================================================================== - */ - - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - @Override - protected IExpression handleForexpr(ForexprContext ctx) { - SimpleforclauseContext simpleForClause = ctx.simpleforclause(); - - // for SimpleForBinding ("," SimpleForBinding)* - int bindingCount = simpleForClause.getChildCount() / 2; - - @NonNull IExpression retval = ObjectUtils.notNull(ctx.exprsingle().accept(this)); - - // step through in reverse - for (int idx = bindingCount - 1; idx >= 0; idx--) { - SimpleforbindingContext simpleForBinding = simpleForClause.simpleforbinding(idx); - - VarnameContext varName = simpleForBinding.varname(); - ExprsingleContext exprSingle = simpleForBinding.exprsingle(); - - Name name = (Name) varName.accept(this); - IExpression boundExpression = exprSingle.accept(this); - - assert name != null; - assert boundExpression != null; - - Let.VariableDeclaration variable = new Let.VariableDeclaration(name, boundExpression); - - retval = new For(variable, retval); - } - return retval; - } - - /* ==================================================================== - * Let Expressions - https://www.w3.org/TR/xpath-31/#id-let-expressions - * ==================================================================== - */ - - @Override - protected IExpression handleLet(LetexprContext context) { - @NonNull IExpression retval = ObjectUtils.notNull(context.exprsingle().accept(this)); - - SimpleletclauseContext letClause = context.simpleletclause(); - List clauses = letClause.simpleletbinding(); - - ListIterator reverseListIterator = clauses.listIterator(clauses.size()); - while (reverseListIterator.hasPrevious()) { - SimpleletbindingContext simpleCtx = reverseListIterator.previous(); - - Name varName = (Name) simpleCtx.varname().accept(this); - IExpression boundExpression = simpleCtx.exprsingle().accept(this); - - assert varName != null; - assert boundExpression != null; - - retval = new Let(varName, boundExpression, retval); // NOPMD intended - } - return retval; - } - - /* ========================================================================= - * Conditional Expressions - https://www.w3.org/TR/xpath-31/#id-conditionals - * ========================================================================= - */ + // ========================================================================= + // Conditional Expressions - https://www.w3.org/TR/xpath-31/#id-conditionals + // ========================================================================= @Override protected IExpression handleIfexpr(IfexprContext ctx) { @@ -816,86 +1100,20 @@ protected IExpression handleIfexpr(IfexprContext ctx) { return new If(testExpr, thenExpr, elseExpr); } - /* ================================================================================== - * Quantified Expressions - https://www.w3.org/TR/xpath-31/#id-quantified-expressions - * ================================================================================== - */ - - @Override - protected IExpression handleQuantifiedexpr(QuantifiedexprContext ctx) { - Quantified.Quantifier quantifier; - int type = ((TerminalNode) ctx.getChild(0)).getSymbol().getType(); - switch (type) { - case Metapath10Lexer.KW_SOME: - quantifier = Quantified.Quantifier.SOME; - break; - case Metapath10Lexer.KW_EVERY: - quantifier = Quantified.Quantifier.EVERY; - break; - default: - throw new UnsupportedOperationException(((TerminalNode) ctx.getChild(0)).getSymbol().getText()); - } - - int numVars = (ctx.getChildCount() - 2) / 5; // children - "satisfies expr" / ", $ varName in expr" - Map vars = new LinkedHashMap<>(); // NOPMD ordering needed - int offset = 0; - for (; offset < numVars; offset++) { - // $ - String varName = ((Name) visit(ctx.varname(offset))).getValue(); - // in - IExpression varExpr = visit(ctx.exprsingle(offset)); - - vars.put(varName, varExpr); - } - - IExpression satisfies = visit(ctx.exprsingle(offset)); - - return new Quantified(quantifier, vars, satisfies); - } - - /* ========================================================================= - * Simple map operator (!) - https://www.w3.org/TR/xpath-31/#id-map-operator - * ========================================================================= - */ + // ========================================================================= + // Simple map operator (!) - https://www.w3.org/TR/xpath-31/#id-map-operator + // ========================================================================= @Override protected IExpression handleSimplemapexpr(SimplemapexprContext context) { return handleGroupedNAiry(context, 0, 2, (ctx, idx, left) -> { + assert left != null; + // the next child is "!" assert "!".equals(ctx.getChild(idx).getText()); - IExpression right = ctx.getChild(idx + 1).accept(this); + IExpression right = ObjectUtils.notNull(ctx.getChild(idx + 1).accept(this)); return new SimpleMap(left, right); }); } - - /* ======================================================================= - * Arrow operator (=>) - https://www.w3.org/TR/xpath-31/#id-arrow-operator - * ======================================================================= - */ - - @Override - protected IExpression handleArrowexpr(ArrowexprContext context) { - // TODO: handle additional syntax for varef and parenthesized - return handleGroupedNAiry(context, 0, 3, (ctx, idx, left) -> { - // the next child is "=>" - assert "=>".equals(ctx.getChild(idx).getText()); - - int offset = (idx - 1) / 3; - - ArrowfunctionspecifierContext fcCtx = ctx.getChild(ArrowfunctionspecifierContext.class, offset); - ArgumentlistContext argumentCtx = ctx.getChild(ArgumentlistContext.class, offset); - // QName name = toQName( - String name = fcCtx.eqname().getText(); - assert name != null; - - try (Stream args = Stream.concat( - Stream.of(left), - parseArgumentList(ObjectUtils.notNull(argumentCtx)))) { - assert args != null; - - return new FunctionCall(name, ObjectUtils.notNull(args.collect(Collectors.toUnmodifiableList()))); - } - }); - } } 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 c5be781ad..9a1168cb1 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 @@ -35,14 +35,17 @@ import gov.nist.secauto.metaschema.core.metapath.cst.math.Multiplication; import gov.nist.secauto.metaschema.core.metapath.cst.math.Subtraction; import gov.nist.secauto.metaschema.core.metapath.cst.path.Axis; +import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem; import gov.nist.secauto.metaschema.core.metapath.cst.path.Flag; import gov.nist.secauto.metaschema.core.metapath.cst.path.ModelInstance; +import gov.nist.secauto.metaschema.core.metapath.cst.path.NameTest; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashOnlyPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.Step; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Wildcard; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -64,6 +67,7 @@ public static String toString(@NonNull IExpression expr) { return new CSTPrinterVisitor().visit(expr); } + @SuppressWarnings("PMD.ExcessivePublicCount") private static final class CSTPrinterVisitor extends AbstractExpressionVisitor { @@ -180,7 +184,7 @@ public String visitFlag(Flag expr, State context) { } @Override - public String visitFunctionCall(FunctionCall expr, State context) { + public String visitFunctionCall(StaticFunctionCall expr, State context) { return appendNode(expr, super.visitFunctionCall(expr, context), context); } @@ -220,7 +224,7 @@ public String visitMultiplication(Multiplication expr, State context) { } @Override - public String visitName(Name expr, State context) { + public String visitName(NameTest expr, State context) { return appendNode(expr, super.visitName(expr, context), context); } @@ -240,7 +244,7 @@ public String visitAxis(Axis expr, State context) { } @Override - public String visitPredicate(Predicate expr, State context) { + public String visitPredicate(PredicateExpression expr, State context) { return appendNode(expr, super.visitPredicate(expr, context), context); } @@ -334,6 +338,40 @@ public String visitSimpleMap(SimpleMap expr, State context) { return appendNode(expr, super.visitSimpleMap(expr, context), context); } + @Override + public String visitArray(ArraySequenceConstructor expr, State context) { + return appendNode(expr, super.visitArray(expr, context), context); + } + + @Override + public String visitArray(ArraySquareConstructor expr, State context) { + return appendNode(expr, super.visitArray(expr, context), context); + } + + @Override + public String visitPostfixLookup(PostfixLookup expr, State context) { + return appendNode(expr, super.visitPostfixLookup(expr, context), context); + } + + @Override + public String visitFunctionCallAccessor(FunctionCallAccessor expr, State context) { + return appendNode(expr, super.visitFunctionCallAccessor(expr, context), context); + } + + @Override + 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/DecimalLiteral.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/DecimalLiteral.java index f9e74f3d0..6cfdad1e4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/DecimalLiteral.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/DecimalLiteral.java @@ -34,6 +34,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of the + * Decimal Literal + * Expression supporting the creation of a Metapath constant literal + * {@link IDecimalItem}. + */ public class DecimalLiteral extends AbstractLiteralExpression { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Except.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Except.java index cacef3b31..7ceab5c41 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Except.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Except.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -55,8 +56,8 @@ public Except(@NonNull IExpression left, @NonNull IExpression right) { @Override protected ISequence applyFilterTo(@NonNull ISequence result, @NonNull List items) { - return ISequence.of(result.asStream() - .filter(item -> !items.contains(item))); + return ISequence.of(ObjectUtils.notNull(result.stream() + .filter(item -> !items.contains(item)))); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ExpressionUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ExpressionUtils.java index 5fc6ec69f..d900ed4ae 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ExpressionUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ExpressionUtils.java @@ -61,7 +61,7 @@ public static Class analyzeStaticResultType retval = baseType; } else { List> expressionClasses = ObjectUtils.notNull(expressions.stream() - .map(expr -> expr.getStaticResultType()).collect(Collectors.toList())); + .map(IExpression::getStaticResultType).collect(Collectors.toList())); // check if the expression classes, are derived from the base type if (checkDerivedFrom(baseType, expressionClasses)) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/For.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/For.java index 41d33a16f..2101bf1ed 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/For.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/For.java @@ -37,6 +37,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of the + * For + * expression supporting variable-based iteration. + */ @SuppressWarnings("PMD.ShortClassName") public class For implements IExpression { @NonNull @@ -44,16 +49,34 @@ public class For implements IExpression { @NonNull private final IExpression returnExpression; + /** + * Construct a new let expression using the provided variable and return clause. + * + * @param variable + * the variable declaration + * @param returnExpr + * the return clause that makes use of variables for evaluation + */ public For(@NonNull VariableDeclaration variable, @NonNull IExpression returnExpr) { this.variable = variable; this.returnExpression = returnExpr; } + /** + * Get the variable declaration. + * + * @return the variable declaration expression + */ @NonNull protected Let.VariableDeclaration getVariable() { return variable; } + /** + * Get the return expression. + * + * @return the return expression + */ @NonNull protected IExpression getReturnExpression() { return returnExpression; @@ -79,9 +102,9 @@ public ISequence accept(DynamicContext dynamicContext, ISequenc List retval = new LinkedList<>(); for (IItem item : variableResult) { - subDynamicContext.bindVariableValue(variable.getName().getValue(), ISequence.of(item)); + subDynamicContext.bindVariableValue(variable.getName(), ISequence.of(item)); retval.addAll(getReturnExpression().accept(subDynamicContext, focus)); } - return ISequence.of(retval); + return ISequence.ofCollection(retval); } } 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 new file mode 100644 index 000000000..9d16f88cb --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCallAccessor.java @@ -0,0 +1,117 @@ +/* + * 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.StaticMetapathException; +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.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class FunctionCallAccessor implements IExpression { + @NonNull + private final IExpression base; + @NonNull + private final IExpression argument; + + /** + * Construct a new functional call accessor. + * + * @param base + * the expression whose result is used as the map or array to perform + * the lookup on + * @param keyOrIndex + * the value to find, which will be the key for a map or the index for + * an array + */ + public FunctionCallAccessor(@NonNull IExpression base, @NonNull IExpression keyOrIndex) { + this.base = base; + this.argument = keyOrIndex; + } + + /** + * Get the base sub-expression. + * + * @return the sub-expression + */ + @NonNull + public IExpression getBase() { + return base; + } + + /** + * Retrieve the argument to use for the lookup. + * + * @return the argument + */ + @NonNull + public IExpression getArgument() { + return argument; + } + + @SuppressWarnings("null") + @Override + public List getChildren() { + return List.of(getBase(), getArgument()); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + ISequence target = getBase().accept(dynamicContext, focus); + IItem collection = target.getFirstItem(true); + IAnyAtomicItem key = FnData.fnData(getArgument().accept(dynamicContext, focus)).getFirstItem(false); + if (key == null) { + throw new StaticMetapathException(StaticMetapathException.NO_FUNCTION_MATCH, + "No key provided for functional call lookup"); + } + + ICollectionValue retval = null; + if (collection instanceof IArrayItem) { + retval = ArrayGet.get((IArrayItem) collection, IIntegerItem.cast(key)); + } else if (collection instanceof IMapItem) { + retval = MapGet.get((IMapItem) collection, key); + } + + return retval == null ? ISequence.empty() : retval.asSequence(); + } + + @Override + public RESULT accept(@NonNull IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitFunctionCallAccessor(this, context); + } +} 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 054fd958b..115179655 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 @@ -35,14 +35,17 @@ import gov.nist.secauto.metaschema.core.metapath.cst.math.Multiplication; import gov.nist.secauto.metaschema.core.metapath.cst.math.Subtraction; import gov.nist.secauto.metaschema.core.metapath.cst.path.Axis; +import gov.nist.secauto.metaschema.core.metapath.cst.path.ContextItem; import gov.nist.secauto.metaschema.core.metapath.cst.path.Flag; import gov.nist.secauto.metaschema.core.metapath.cst.path.ModelInstance; +import gov.nist.secauto.metaschema.core.metapath.cst.path.NameTest; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RelativeSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootDoubleSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashOnlyPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.RootSlashPath; import gov.nist.secauto.metaschema.core.metapath.cst.path.Step; +import gov.nist.secauto.metaschema.core.metapath.cst.path.Wildcard; import edu.umd.cs.findbugs.annotations.NonNull; @@ -56,6 +59,7 @@ * @param * additional state to pass between nodes visited */ +@SuppressWarnings("PMD.ExcessivePublicCount") public interface IExpressionVisitor { /** @@ -188,7 +192,7 @@ public interface IExpressionVisitor { * the processing context * @return the visitation result or {@code null} if no result was produced */ - RESULT visitFunctionCall(@NonNull FunctionCall expr, @NonNull CONTEXT context); + RESULT visitFunctionCall(@NonNull StaticFunctionCall expr, @NonNull CONTEXT context); /** * Visit the CST node. @@ -276,7 +280,7 @@ public interface IExpressionVisitor { * the processing context * @return the visitation result or {@code null} if no result was produced */ - RESULT visitName(@NonNull Name expr, @NonNull CONTEXT context); + RESULT visitName(@NonNull NameTest expr, @NonNull CONTEXT context); /** * Visit the CST node. @@ -309,7 +313,7 @@ public interface IExpressionVisitor { * the processing context * @return the visitation result or {@code null} if no result was produced */ - RESULT visitPredicate(@NonNull Predicate expr, @NonNull CONTEXT context); + RESULT visitPredicate(@NonNull PredicateExpression expr, @NonNull CONTEXT context); /** * Visit the CST node. @@ -508,4 +512,81 @@ public interface IExpressionVisitor { * @return the visitation result or {@code null} if no result was produced */ RESULT visitSimpleMap(@NonNull SimpleMap 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 visitMapConstructor(@NonNull MapConstructor 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 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. + * + * @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 visitPostfixLookup(@NonNull PostfixLookup 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 visitFunctionCallAccessor(@NonNull FunctionCallAccessor 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 visitUnaryLookup(@NonNull UnaryLookup expr, @NonNull CONTEXT context); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IntegerLiteral.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IntegerLiteral.java index 9a86b0119..102a92b08 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IntegerLiteral.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/IntegerLiteral.java @@ -34,6 +34,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of the + * Integer Literal + * Expression supporting the creation of a Metapath constant literal + * {@link IIntegerItem}. + */ public class IntegerLiteral extends AbstractLiteralExpression { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Intersect.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Intersect.java index 04999c6de..10dad12a0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Intersect.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Intersect.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -56,9 +57,9 @@ public Intersect(@NonNull IExpression left, @NonNull IExpression right) { @Override protected ISequence applyFilterTo(@NonNull ISequence result, @NonNull List items) { - return ISequence.of(result.asStream() + return ISequence.of(ObjectUtils.notNull(result.stream() .distinct() - .filter(items::contains)); + .filter(items::contains))); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Let.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Let.java index bd7389bb4..393195949 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Let.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Let.java @@ -33,6 +33,8 @@ import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -57,7 +59,7 @@ public class Let implements IExpression { * @param returnExpression * the inner expression to evaluate with the variable in-scope */ - public Let(@NonNull Name name, @NonNull IExpression boundExpression, @NonNull IExpression returnExpression) { + public Let(@NonNull QName name, @NonNull IExpression boundExpression, @NonNull IExpression returnExpression) { this.variable = new VariableDeclaration(name, boundExpression); this.returnExpression = returnExpression; } @@ -102,13 +104,25 @@ public ISequence accept(DynamicContext dynamicContext, ISequenc return getReturnExpression().accept(subDynamicContext, focus); } + /** + * A Metapath expression that binds a variable name to an expresssion. + */ public static class VariableDeclaration { @NonNull - private final Name name; + private final QName name; @NonNull private final IExpression boundExpression; - public VariableDeclaration(@NonNull Name name, @NonNull IExpression boundExpression) { + /** + * Construct a new variable declaration, binding the provided variable name to + * the bound expression. + * + * @param name + * trhe variable name + * @param boundExpression + * the bound expression + */ + public VariableDeclaration(@NonNull QName name, @NonNull IExpression boundExpression) { this.name = name; this.boundExpression = boundExpression; } @@ -119,7 +133,7 @@ public VariableDeclaration(@NonNull Name name, @NonNull IExpression boundExpress * @return the variable name */ @NonNull - public Name getName() { + public QName getName() { return name; } @@ -133,15 +147,27 @@ public IExpression getBoundExpression() { return boundExpression; } + /** + * Bind the variable name to the evaluation result of the bound expression. + * + * @param evaluationDynamicContext + * the {@link DynamicContext} used to evaluate the bound expression + * @param focus + * the evaluation focus to use to evaluate the bound expression + * @param boundDynamicContext + * the {@link DynamicContext} the variable is bound to + */ public void bind( - @NonNull DynamicContext evalContext, + @NonNull DynamicContext evaluationDynamicContext, @NonNull ISequence focus, - @NonNull DynamicContext boundContext) { + @NonNull DynamicContext boundDynamicContext) { + + ISequence result = getBoundExpression().accept(evaluationDynamicContext, focus); - ISequence result = getBoundExpression().accept(evalContext, focus); + // ensure this sequence is list backed + result.getValue(); - String name = getName().getValue(); - boundContext.bindVariableValue(name, result); + boundDynamicContext.bindVariableValue(getName(), result); } } } 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..65f98f8d9 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/MapConstructor.java @@ -0,0 +1,154 @@ +/* + * 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.InvalidTypeMetapathException; +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; + +/** + * An implementation of the + * Map + * Constructor supporting the creation of a Metapath {@link IMapItem}. + */ +public class MapConstructor implements IExpression { + @NonNull + private final List entries; + + /** + * Construct a new map constructor expression that uses the provided entry + * expressions to initialize the map entries. + * + * @param entries + * the expressions used to produce the map 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 -> { + IExpression keyExpression = item.getKeyExpression(); + IAnyAtomicItem key = FnData.fnData(keyExpression.accept(dynamicContext, focus)) + .getFirstItem(true); + if (key == null) { + throw new InvalidTypeMetapathException(null, String.format( + "The expression '%s' did not result in a single key atomic value.", keyExpression.toASTString())); + } + 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); + } + + /** + * A map entry expression used to produce an entry in a {@link IMapItem}. + */ + public static class Entry implements IExpression { + @NonNull + private final IExpression keyExpression; + @NonNull + private final IExpression valueExpression; + + /** + * Construct a new map entry expression using the provided ket and value + * expressions. + * + * @param keyExpression + * the expression used to get the map entry key + * @param valueExpression + * the expression used to get the map entry value + */ + public Entry(@NonNull IExpression keyExpression, @NonNull IExpression valueExpression) { + this.keyExpression = keyExpression; + this.valueExpression = valueExpression; + } + + /** + * Get the map entry key expression. + * + * @return the key expression + */ + @NonNull + public IExpression getKeyExpression() { + return keyExpression; + } + + /** + * Get the map entry value expression. + * + * @return the value expression + */ + @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/cst/Metapath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Metapath.java index 8abb5a942..f77150838 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Metapath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Metapath.java @@ -68,7 +68,7 @@ public ISequence accept(DynamicContext dynamicContext, ISequence focus) { Stream retval = ObjectUtils.notNull(getChildren().stream() .flatMap(child -> { ISequence result = child.accept(dynamicContext, focus); - return result.asStream(); + return result.stream(); })); return ISequence.of(retval); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PostfixLookup.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PostfixLookup.java new file mode 100644 index 000000000..259ab0612 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PostfixLookup.java @@ -0,0 +1,104 @@ +/* + * 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.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; +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; + +/** + * An implementation of + * Postfix Lookup + * Operators supporting access to items in Metapath maps and arrays. + *

+ * Provides support for various types of key- and index-based lookups related to + * {@link IMapItem} and {@link IArrayItem} objects. + */ +public class PostfixLookup + extends AbstractLookup { + + @NonNull + private final IExpression base; + + /** + * Construct a new postfix lookup expression that uses the provided key + * specifier. + * + * @param base + * the base expression used to get the target of the lookup + * @param keySpecifier + * the key specifier used to determine matching entries + */ + public PostfixLookup(@NonNull IExpression base, @NonNull IKeySpecifier keySpecifier) { + super(keySpecifier); + this.base = base; + } + + /** + * Get the base sub-expression. + * + * @return the sub-expression + */ + @NonNull + public IExpression getBase() { + return base; + } + + @SuppressWarnings("null") + @Override + public List getChildren() { + return List.of(getBase()); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + ISequence base = getBase().accept(dynamicContext, focus); + + IKeySpecifier specifier = getKeySpecifier(); + + return ISequence.of(ObjectUtils.notNull(base.stream() + .flatMap(item -> { + assert item != null; + return specifier.lookup(item, dynamicContext, focus); + }) + .flatMap(ICollectionValue::normalizeAsItems))); + } + + @Override + public RESULT accept(@NonNull IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitPostfixLookup(this, context); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Predicate.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PredicateExpression.java similarity index 95% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Predicate.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PredicateExpression.java index 7881452e7..cca0a474c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Predicate.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/PredicateExpression.java @@ -36,13 +36,14 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; -public class Predicate implements IExpression { +public class PredicateExpression implements IExpression { @NonNull private final IExpression base; @NonNull @@ -56,7 +57,7 @@ public class Predicate implements IExpression { * @param predicates * the expression(s) to apply as a filter */ - public Predicate(@NonNull IExpression base, @NonNull List predicates) { + public PredicateExpression(@NonNull IExpression base, @NonNull List predicates) { this.base = base; this.predicates = predicates; } @@ -98,7 +99,7 @@ public List getChildren() { AtomicInteger index = new AtomicInteger(); Stream stream = ObjectUtils.notNull( - retval.asStream().map(item -> { + retval.stream().map(item -> { // build a positional index of the items return Map.entry(BigInteger.valueOf(index.incrementAndGet()), item); }).filter(entry -> { @@ -125,7 +126,7 @@ public List getChildren() { } return bool; }).anyMatch(x -> !x); - }).map(entry -> entry.getValue())); + }).map(Entry::getValue)); retval = ISequence.of(stream); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Quantified.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Quantified.java index 04093a970..8a414cee0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Quantified.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Quantified.java @@ -44,6 +44,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class Quantified @@ -56,7 +58,7 @@ public enum Quantifier { @NonNull private final Quantifier quantifier; @NonNull - private final Map inClauses; + private final Map inClauses; @NonNull private final IExpression satisfies; @@ -74,7 +76,7 @@ public enum Quantifier { */ public Quantified( @NonNull Quantifier quantifier, - @NonNull Map inClauses, + @NonNull Map inClauses, @NonNull IExpression satisfies) { this.quantifier = quantifier; this.inClauses = inClauses; @@ -98,7 +100,7 @@ public Quantifier getQuantifier() { * @return the variable names mapped to the associated Metapath expression */ @NonNull - public Map getInClauses() { + public Map getInClauses() { return inClauses; } @@ -122,20 +124,20 @@ public List getChildren() { @SuppressWarnings("PMD.SystemPrintln") @Override public ISequence accept(DynamicContext dynamicContext, ISequence focus) { - Map> clauses = getInClauses().entrySet().stream() + Map> clauses = getInClauses().entrySet().stream() .map(entry -> Map.entry( entry.getKey(), entry.getValue().accept(dynamicContext, focus))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - List clauseKeys = new ArrayList<>(clauses.keySet()); + List clauseKeys = new ArrayList<>(clauses.keySet()); List> clauseValues = new ArrayList<>(clauses.values()); boolean retval = true; for (List product : new CartesianProduct<>(clauseValues)) { DynamicContext subDynamicContext = dynamicContext.subContext(); for (int idx = 0; idx < product.size(); idx++) { - String var = clauseKeys.get(idx); + QName var = clauseKeys.get(idx); IItem item = product.get(idx); assert var != null; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Range.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Range.java index a0914210e..f89d13286 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Range.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Range.java @@ -84,7 +84,7 @@ public ISequence accept(DynamicContext dynamicContext, ISequenceSimple Map Operator + * ! supporting evaluating a right expression against every + * item in a sequence produced by a left expression. + */ public class SimpleMap extends AbstractBinaryExpression { + /** + * Construct a simple map expression. + * + * @param left + * the expression used to generate the right sequence + * @param right + * the expression used to evaluate each item in the right sequence + */ public SimpleMap(@NonNull IExpression left, @NonNull IExpression right) { super(left, right); } @@ -45,7 +59,7 @@ public ISequence accept(DynamicContext dynamicContext, ISequence focus) { IExpression right = getRight(); return ObjectUtils.notNull(leftResult.stream() - .flatMap(item -> right.accept(dynamicContext, ISequence.of(item)).asStream()) + .flatMap(item -> right.accept(dynamicContext, ISequence.of(item)).stream()) .collect(ISequence.toSequence())); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCall.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StaticFunctionCall.java similarity index 95% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCall.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StaticFunctionCall.java index dbdcf6ea9..9660aeaa3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/FunctionCall.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StaticFunctionCall.java @@ -38,11 +38,13 @@ import java.util.Objects; import java.util.stream.Collectors; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; -public class FunctionCall implements IExpression { +public class StaticFunctionCall implements IExpression { @NonNull - private final String name; + private final QName name; @NonNull private final List arguments; private IFunction function; @@ -55,7 +57,7 @@ public class FunctionCall implements IExpression { * @param arguments * the expressions used to provide arguments to the function call */ - public FunctionCall(@NonNull String name, @NonNull List arguments) { + public StaticFunctionCall(@NonNull QName name, @NonNull List arguments) { this.name = Objects.requireNonNull(name, "name"); this.arguments = Objects.requireNonNull(arguments, "arguments"); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringConcat.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringConcat.java index 2f7c36f27..757163ee9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringConcat.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringConcat.java @@ -71,7 +71,7 @@ public ISequence accept(DynamicContext dynamicContext, ISequence focus) { StringBuilder builder = new StringBuilder(); for (IExpression child : getChildren()) { ISequence result = child.accept(dynamicContext, focus); - FnData.fnData(result).asStream() + FnData.fnData(result).stream() .forEachOrdered(item -> { builder.append(item.asString()); }); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringLiteral.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringLiteral.java index 5d0fbaabe..9ada58e64 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringLiteral.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/StringLiteral.java @@ -35,6 +35,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of the + * String Literal + * Expression supporting the creation of a Metapath constant literal + * {@link IStringItem}. + */ public class StringLiteral extends AbstractLiteralExpression { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/UnaryLookup.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/UnaryLookup.java new file mode 100644 index 000000000..5378aa65f --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/UnaryLookup.java @@ -0,0 +1,84 @@ +/* + * 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.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IKeySpecifier; +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; + +/** + * An implementation of + * Unary Lookup + * Operators supporting access to items in Metapath maps and arrays. + *

+ * Provides support for various types of key- and index-based lookups related to + * {@link IMapItem} and {@link IArrayItem} objects. + */ +public class UnaryLookup + extends AbstractLookup { + /** + * Construct a new unary lookup expression that uses the provided key specifier. + * + * @param keySpecifier + * the key specifier used to determine matching entries + */ + public UnaryLookup(@NonNull IKeySpecifier keySpecifier) { + super(keySpecifier); + } + + @SuppressWarnings("null") + @Override + public List getChildren() { + return List.of(); + } + + @Override + public ISequence accept(DynamicContext dynamicContext, ISequence focus) { + IKeySpecifier specifier = getKeySpecifier(); + + return ISequence.of(ObjectUtils.notNull(focus.stream() + .flatMap(item -> { + assert item != null; + return specifier.lookup(item, dynamicContext, focus); + }) + .flatMap(ICollectionValue::normalizeAsItems))); + } + + @Override + public RESULT accept(@NonNull IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitUnaryLookup(this, context); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Union.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Union.java index 7eaacd691..ab8be293f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Union.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Union.java @@ -70,11 +70,14 @@ public RESULT accept(IExpressionVisitor visit @Override public ISequence accept(DynamicContext dynamicContext, ISequence focus) { - focus.collect(); + // ensure the sequence is backed by a list + focus.getValue(); + + // now process the union @NonNull Stream retval = ObjectUtils.notNull(getChildren().stream() .flatMap(child -> { ISequence result = child.accept(dynamicContext, focus); - return result.asStream(); + return result.stream(); }).distinct()); return ISequence.of(retval); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/VariableReference.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/VariableReference.java index 9cd5beb7e..959861c84 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/VariableReference.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/VariableReference.java @@ -34,6 +34,8 @@ import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -43,7 +45,7 @@ */ public class VariableReference implements IExpression { @NonNull - private final Name name; + private final QName name; /** * Construct a new Metapath variable reference CST node. @@ -51,7 +53,7 @@ public class VariableReference implements IExpression { * @param name * the variable name */ - public VariableReference(@NonNull Name name) { + public VariableReference(@NonNull QName name) { this.name = name; } @@ -61,7 +63,7 @@ public VariableReference(@NonNull Name name) { * @return the variable name */ @NonNull - public Name getName() { + public QName getName() { return name; } @@ -73,7 +75,7 @@ public List getChildren() { @SuppressWarnings("null") @Override public String toASTString() { - return String.format("%s[name=%s]", getClass().getName(), getName().getValue()); + return String.format("%s[name=%s]", getClass().getName(), getName()); } @Override @@ -83,7 +85,7 @@ public RESULT accept(IExpressionVisitor visit @Override public ISequence accept(DynamicContext dynamicContext, ISequence focus) { - return dynamicContext.getVariableValue(ObjectUtils.notNull(getName().getValue())); + return dynamicContext.getVariableValue(ObjectUtils.notNull(getName())); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractPathExpression.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractPathExpression.java index 410e900f0..9f58eeaaa 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractPathExpression.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/AbstractPathExpression.java @@ -35,6 +35,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; import gov.nist.secauto.metaschema.core.metapath.item.node.ICycledAssemblyNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.stream.Stream; @@ -68,15 +69,15 @@ protected Stream searchExpression( @NonNull IExpression expression, @NonNull DynamicContext dynamicContext, @NonNull ISequence outerFocus) { - - outerFocus.collect(); + // ensure the sequence is backed by a list + outerFocus.getValue(); // check the current focus @SuppressWarnings("unchecked") Stream nodeMatches - = (Stream) expression.accept(dynamicContext, outerFocus).asStream(); + = (Stream) expression.accept(dynamicContext, outerFocus).stream(); - Stream childMatches = outerFocus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) + Stream childMatches = outerFocus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) .flatMap(focusedNode -> { Stream matches; @@ -92,7 +93,7 @@ protected Stream searchExpression( matches = searchExpression( expression, dynamicContext, - ISequence.of(Stream.concat(flags, modelItems))); + ISequence.of(ObjectUtils.notNull(Stream.concat(flags, modelItems)))); } return matches; }); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Axis.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Axis.java index 1895941a9..dd1340d87 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Axis.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Axis.java @@ -43,7 +43,7 @@ @SuppressWarnings("PMD.ShortClassName") // intentional public enum Axis implements IExpression { - SELF(focus -> Stream.of(focus)), + SELF(Stream::of), PARENT(focus -> Stream.ofNullable(focus.getParentNodeItem())), ANCESTOR(INodeItem::ancestor), ANCESTOR_OR_SELF(INodeItem::ancestorOrSelf), @@ -98,12 +98,12 @@ public ISequence accept( if (outerFocus.isEmpty()) { retval = ISequence.empty(); } else { - retval = ISequence.of(outerFocus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) + retval = ISequence.of(ObjectUtils.notNull(outerFocus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) .flatMap(item -> { assert item != null; return execute(item); - }).distinct()); + }).distinct())); } return retval; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ContextItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java similarity index 89% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ContextItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java index 79ec54571..beb9169bf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/ContextItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ContextItem.java @@ -24,12 +24,13 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath.cst; +package gov.nist.secauto.metaschema.core.metapath.cst.path; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException; import gov.nist.secauto.metaschema.core.metapath.ISequence; -import gov.nist.secauto.metaschema.core.metapath.cst.path.AbstractPathExpression; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import java.util.Collections; @@ -37,6 +38,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of the + * Context + * Item Expression based on the current focus of the Metapath + * {@link DynamicContext}. + */ public final class ContextItem extends AbstractPathExpression { @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Flag.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Flag.java index 60d9e2a46..0a959feac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Flag.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Flag.java @@ -31,13 +31,15 @@ import gov.nist.secauto.metaschema.core.metapath.cst.AbstractNamedInstanceExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; -import gov.nist.secauto.metaschema.core.metapath.cst.Name; import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; import gov.nist.secauto.metaschema.core.metapath.item.node.IFlagNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.stream.Stream; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; public class Flag // NOPMD - intentional name @@ -68,12 +70,12 @@ public RESULT accept(IExpressionVisitor visit public ISequence accept( DynamicContext dynamicContext, ISequence focus) { - return ISequence.of(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) + return ISequence.of(ObjectUtils.notNull(focus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) .flatMap(item -> { assert item != null; return match(item); - })); + }))); } /** @@ -87,8 +89,8 @@ public ISequence accept( @NonNull protected Stream match(@NonNull INodeItem focusedItem) { Stream retval; - if (getTest() instanceof Name) { - String name = ((Name) getTest()).getValue(); + if (getTest() instanceof NameTest) { + QName name = ((NameTest) getTest()).getName(); IFlagNodeItem item = focusedItem.getFlagByName(name); retval = item == null ? Stream.empty() : Stream.of(item); diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingContainerModelAssembly.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INameTestExpression.java similarity index 75% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingContainerModelAssembly.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INameTestExpression.java index 47f9d2144..992a5211e 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingContainerModelAssembly.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INameTestExpression.java @@ -24,21 +24,28 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.metaschema; +package gov.nist.secauto.metaschema.core.metapath.cst.path; -import gov.nist.secauto.metaschema.core.model.IContainerModelAssembly; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import java.util.Collections; import java.util.List; -import java.util.Map; - -public interface IBindingContainerModelAssembly extends IContainerModelAssembly, IBindingContainerModelAbsolute { +public interface INameTestExpression extends INodeTestExpression { + @SuppressWarnings("null") @Override - List getChoiceInstances(); + default List getChildren() { + return Collections.emptyList(); + } @Override - IInstanceModelChoiceGroupBinding getChoiceGroupInstanceByName(String name); + default Class getBaseResultType() { + return INodeItem.class; + } @Override - Map getChoiceGroupInstances(); + default Class getStaticResultType() { + return getBaseResultType(); + } } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingInstanceFlag.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INodeTestExpression.java similarity index 89% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingInstanceFlag.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INodeTestExpression.java index 6c127ea7b..b5bd5bed1 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IBindingInstanceFlag.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/INodeTestExpression.java @@ -24,10 +24,10 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.metaschema; +package gov.nist.secauto.metaschema.core.metapath.cst.path; -import gov.nist.secauto.metaschema.core.model.IFlagInstance; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; -public interface IBindingInstanceFlag extends IFlagInstance, IBindingInstance { +public interface INodeTestExpression extends IExpression { // no additional methods } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ModelInstance.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ModelInstance.java index f63c0a7f6..9b63f740d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ModelInstance.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/ModelInstance.java @@ -31,14 +31,16 @@ import gov.nist.secauto.metaschema.core.metapath.cst.AbstractNamedInstanceExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; -import gov.nist.secauto.metaschema.core.metapath.cst.Name; import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; import gov.nist.secauto.metaschema.core.metapath.item.node.IModelNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; import java.util.stream.Stream; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; @SuppressWarnings("rawtypes") @@ -70,12 +72,12 @@ public RESULT accept(IExpressionVisitor visit public ISequence> accept( DynamicContext dynamicContext, ISequence focus) { - return ISequence.of(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) + return ISequence.of(ObjectUtils.notNull(focus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) .flatMap(item -> { assert item != null; return match(item); - })); + }))); } /** @@ -90,8 +92,8 @@ public RESULT accept(IExpressionVisitor visit protected Stream> match( @NonNull INodeItem focusedItem) { Stream> retval; - if (getTest() instanceof Name) { - String name = ((Name) getTest()).getValue(); + if (getTest() instanceof NameTest) { + QName name = ((NameTest) getTest()).getName(); List> items = focusedItem.getModelItemsByName(name); retval = items.stream(); } else { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Name.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameTest.java similarity index 76% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Name.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameTest.java index 2760a5849..b4f52d3a7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/Name.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/NameTest.java @@ -24,16 +24,17 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath.cst; +package gov.nist.secauto.metaschema.core.metapath.cst.path; import gov.nist.secauto.metaschema.core.metapath.DynamicContext; import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; -import gov.nist.secauto.metaschema.core.util.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import java.util.List; +import javax.xml.namespace.QName; import edu.umd.cs.findbugs.annotations.NonNull; @@ -42,20 +43,21 @@ * expanded QName * name test. */ -public class Name // NOPMD - intentional - implements IExpression { +@SuppressWarnings("PMD.TestClassWithoutTestCases") +public class NameTest + implements INameTestExpression { @NonNull - private final String value; + private final QName name; /** * Construct a new expanded QName-based literal expression. * - * @param value + * @param name * the literal value */ - public Name(@NonNull String value) { - this.value = value; + public NameTest(@NonNull QName name) { + this.name = name; } /** @@ -64,23 +66,8 @@ public Name(@NonNull String value) { * @return the string value of the name */ @NonNull - public String getValue() { - return value; - } - - @Override - public List getChildren() { - return CollectionUtil.emptyList(); - } - - @Override - public Class getBaseResultType() { - return INodeItem.class; - } - - @Override - public Class getStaticResultType() { - return getBaseResultType(); + public QName getName() { + return name; } @Override @@ -92,20 +79,20 @@ public RESULT accept(IExpressionVisitor visit public ISequence accept( DynamicContext dynamicContext, ISequence focus) { - return ISequence.of(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) - .filter(this::match)); + return ISequence.of(ObjectUtils.notNull(focus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) + .filter(this::match))); } @SuppressWarnings("PMD.UnusedPrivateMethod") private boolean match(INodeItem item) { return item instanceof IDefinitionNodeItem - && getValue().equals(((IDefinitionNodeItem) item).getName()); + && getName().equals(((IDefinitionNodeItem) item).getName()); } @SuppressWarnings("null") @Override public String toASTString() { - return String.format("%s[value=%s]", getClass().getName(), getValue()); + return String.format("%s[name=%s]", getClass().getName(), getName()); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RelativeSlashPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RelativeSlashPath.java index f65b0ba61..1b52e18a4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RelativeSlashPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RelativeSlashPath.java @@ -60,6 +60,9 @@ public ISequence accept( ISequence focus) { ISequence leftResult = getLeft().accept(dynamicContext, focus); + // ensure the left sequence is list backed + leftResult.getValue(); + return getRight().accept(dynamicContext, leftResult); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java index c5e5902ea..aa0d0688e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashOnlyPath.java @@ -60,8 +60,8 @@ public ISequence accept( DynamicContext dynamicContext, ISequence focus) { - return ObjectUtils.notNull(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) + return ObjectUtils.notNull(focus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) .map(item -> Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)).findFirst().get()) .collect(ISequence.toSequence())); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java index c33b16189..4d0cec884 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/RootSlashPath.java @@ -59,9 +59,10 @@ public ISequence accept( DynamicContext dynamicContext, ISequence focus) { - ISequence roots = ObjectUtils.notNull(focus.asStream() - .map(item -> ItemUtils.checkItemIsNodeItemForStep(item)) - .map(item -> Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)).findFirst().get()) + ISequence roots = ObjectUtils.notNull(focus.stream() + .map(ItemUtils::checkItemIsNodeItemForStep) + // the previous checks for a null instance + .flatMap(item -> Axis.ANCESTOR_OR_SELF.execute(ObjectUtils.notNull(item)).limit(1)) .collect(ISequence.toSequence())); return getExpression().accept(dynamicContext, roots); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java index 41178b2ae..5e398af78 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Step.java @@ -49,7 +49,7 @@ public class Step implements IExpression { // NOPMD - intentional @NonNull private final Axis axisExpression; @NonNull - private final IExpression stepExpression; + private final INodeTestExpression stepExpression; @NonNull private final Class staticResultType; @@ -62,7 +62,7 @@ public class Step implements IExpression { // NOPMD - intentional * the sub-expression to evaluate before filtering with the predicates */ @SuppressWarnings("null") - public Step(@NonNull Axis axis, @NonNull IExpression step) { + public Step(@NonNull Axis axis, @NonNull INodeTestExpression step) { this.axisExpression = axis; this.stepExpression = step; this.staticResultType = ExpressionUtils.analyzeStaticResultType(IItem.class, List.of(step)); @@ -84,7 +84,7 @@ public Axis getAxis() { * @return the sub-expression */ @NonNull - public IExpression getStep() { + public INodeTestExpression getStep() { return stepExpression; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Wildcard.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Wildcard.java new file mode 100644 index 000000000..ba666d16f --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/cst/path/Wildcard.java @@ -0,0 +1,127 @@ +/* + * 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.path; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpressionVisitor; +import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils; +import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem; +import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * The CST node for a Metapath + * wildcard name + * test. + */ +public class Wildcard implements INameTestExpression { + @Nullable + private final Predicate> matcher; + + /** + * Construct a new wildcard name test expression using the provided matcher. + * + * @param matcher + * the matcher used to determine matching nodes + */ + public Wildcard(@Nullable Predicate> matcher) { + this.matcher = matcher; + } + + @Override + public RESULT accept(IExpressionVisitor visitor, CONTEXT context) { + return visitor.visitWildcard(this, context); + } + + @Override + public ISequence accept( + DynamicContext dynamicContext, ISequence focus) { + Stream nodes = focus.stream().map(ItemUtils::checkItemIsNodeItemForStep); + if (matcher != null) { + Predicate> test = matcher; + nodes = nodes.filter(item -> { + assert matcher != null; + return !(item instanceof IDefinitionNodeItem) || + test.test((IDefinitionNodeItem) item); + }); + } + return ISequence.of(ObjectUtils.notNull(nodes)); + } + + /** + * A wildcard matcher that matches a specific local name in any namespace. + */ + public static class MatchAnyNamespace implements Predicate> { + @NonNull + private final String localName; + + /** + * Construct the matcher using the provided local name for matching. + * + * @param localName + * the name used to match nodes + */ + public MatchAnyNamespace(@NonNull String localName) { + this.localName = localName; + } + + @Override + public boolean test(IDefinitionNodeItem item) { + return localName.equals(item.getName().getLocalPart()); + } + } + + /** + * A wildcard matcher that matches any local name in a specific namespace. + */ + public static class MatchAnyLocalName implements Predicate> { + @NonNull + private final String namespace; + + /** + * Construct the matcher using the provided namespace for matching. + * + * @param namespace + * the namespace used to match nodes + */ + public MatchAnyLocalName(@NonNull String namespace) { + this.namespace = namespace; + } + + @Override + public boolean test(IDefinitionNodeItem item) { + return namespace.equals(item.getName().getNamespaceURI()); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/format/MetapathFormatter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/format/MetapathFormatter.java index 48af77792..2b1cf765f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/format/MetapathFormatter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/format/MetapathFormatter.java @@ -35,6 +35,7 @@ import gov.nist.secauto.metaschema.core.metapath.item.node.IModuleNodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.metapath.item.node.IRootAssemblyNodeItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import edu.umd.cs.findbugs.annotations.NonNull; @@ -60,7 +61,7 @@ public String formatDocument(IDocumentNodeItem document) { @Override public String formatRootAssembly(IRootAssemblyNodeItem root) { - return root.getName(); + return ObjectUtils.notNull(root.getName().getLocalPart()); } @Override @@ -88,7 +89,7 @@ public String formatFlag(IFlagNodeItem flag) { @SuppressWarnings("null") @NonNull private static String formatModelPathSegment(@NonNull IModelNodeItem item) { - StringBuilder builder = new StringBuilder(item.getName()) + StringBuilder builder = new StringBuilder(item.getName().getLocalPart()) .append('[') .append(item.getPosition()) .append(']'); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/AbstractFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/AbstractFunction.java index d639a5ec1..b9237cdff 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/AbstractFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/AbstractFunction.java @@ -28,13 +28,13 @@ import java.util.List; +import javax.xml.namespace.QName; + import edu.umd.cs.findbugs.annotations.NonNull; abstract class AbstractFunction implements IFunction { @NonNull - private final String name; - @NonNull - private final String namespace; + private final QName qname; @NonNull private final List arguments; @@ -42,19 +42,19 @@ protected AbstractFunction( @NonNull String name, @NonNull String namespace, @NonNull List arguments) { - this.name = name; - this.namespace = namespace; - this.arguments = arguments; + this(new QName(namespace, name), arguments); } - @Override - public String getName() { - return name; + protected AbstractFunction( + @NonNull QName qname, + @NonNull List arguments) { + this.qname = qname; + this.arguments = arguments; } @Override - public String getNamespace() { - return namespace; + public QName getQName() { + return qname; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java index 0e153d284..cfde0d34c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ArithmeticFunctionException.java @@ -96,7 +96,7 @@ public ArithmeticFunctionException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FOAR"; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ComparisonFunctions.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ComparisonFunctions.java index dc2c82e91..9c2509a13 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ComparisonFunctions.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ComparisonFunctions.java @@ -47,6 +47,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; +@SuppressWarnings({ "PMD.GodClass", "PMD.CyclomaticComplexity" }) public final class ComparisonFunctions { /** * Comparison operators. @@ -117,9 +118,9 @@ public static IBooleanItem generalCompairison( // NOPMD - acceptable complexity @NonNull ISequence rightItems) { IBooleanItem retval = IBooleanItem.FALSE; - for (IAnyAtomicItem left : leftItems.asList()) { + for (IAnyAtomicItem left : leftItems.getValue()) { assert left != null; - for (IAnyAtomicItem right : rightItems.asList()) { + for (IAnyAtomicItem right : rightItems.getValue()) { assert right != null; IAnyAtomicItem leftCast; IAnyAtomicItem rightCast; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java index a67b5f508..38c007e78 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DateTimeFunctionException.java @@ -102,7 +102,7 @@ public DateTimeFunctionException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FODT"; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java index 8907d8d66..fbf5d1d13 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DefaultFunction.java @@ -32,9 +32,11 @@ import gov.nist.secauto.metaschema.core.metapath.MetapathException; 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.TypeSystem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.ArrayList; import java.util.Collections; @@ -43,7 +45,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; +import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -192,70 +194,38 @@ public static List> convertArguments( String.format("argument signature doesn't match '%s'", function.toSignature())); } - assert argument != null; - ISequence parameter = parametersIterator.next(); + assert argument != null; + assert parameter != null; - int size = parameter.size(); - Occurrence occurrence = argument.getSequenceType().getOccurrence(); - switch (occurrence) { - case ONE: { - if (size != 1) { - throw new InvalidTypeMetapathException( - null, - String.format("a sequence of one expected, but found '%d'", size)); - } + retval.add(convertArgument(argument, parameter)); + } + return retval; + } - IItem item = FunctionUtils.getFirstItem(parameter, true); - parameter = item == null ? ISequence.empty() : ISequence.of(item); - break; - } - case ZERO_OR_ONE: { - if (size > 1) { - throw new InvalidTypeMetapathException( - null, - String.format("a sequence of zero or one expected, but found '%d'", size)); - } + @NonNull + private static ISequence convertArgument( + @NonNull IArgument argument, + @NonNull ISequence parameter) { + // apply occurrence + ISequence retval = argument.getSequenceType().getOccurrence().getSequenceHandler().handle(parameter); - IItem item = FunctionUtils.getFirstItem(parameter, false); - parameter = item == null ? ISequence.empty() : ISequence.of(item); - break; - } - case ONE_OR_MORE: - if (size < 1) { - throw new InvalidTypeMetapathException( - null, - String.format("a sequence of zero or more expected, but found '%d'", size)); - } - break; - case ZERO: - if (size != 0) { - throw new InvalidTypeMetapathException( - null, - String.format("an empty sequence expected, but found '%d'", size)); - } - break; - case ZERO_OR_MORE: - default: - // do nothing - } + // apply function conversion and type promotion to the parameter + if (!retval.isEmpty()) { + retval = convertSequence(argument, retval); + // verify resulting values Class argumentClass = argument.getSequenceType().getType(); - - // apply function conversion and type promotion to the parameter - parameter = convertSequence(argument, parameter); - - // check resulting values - for (IItem item : parameter.asList()) { + for (IItem item : retval.getValue()) { Class itemClass = item.getClass(); if (!argumentClass.isAssignableFrom(itemClass)) { throw new InvalidTypeMetapathException( item, - String.format("The type '%s' is not a subtype of '%s'", itemClass.getName(), argumentClass.getName())); + String.format("The type '%s' is not a subtype of '%s'", + TypeSystem.getName(itemClass), + TypeSystem.getName(argumentClass))); } } - - retval.add(parameter); } return retval; } @@ -273,44 +243,38 @@ public static List> convertArguments( */ @NonNull protected static ISequence convertSequence(@NonNull IArgument argument, @NonNull ISequence sequence) { - @NonNull ISequence retval; - if (sequence.isEmpty()) { - retval = ISequence.empty(); - } else { - ISequenceType requiredSequenceType = argument.getSequenceType(); - Class requiredSequenceTypeClass = requiredSequenceType.getType(); - - List result = new ArrayList<>(sequence.size()); - - boolean atomize = IAnyAtomicItem.class.isAssignableFrom(requiredSequenceTypeClass); - - for (IItem item : sequence.asList()) { - assert item != null; - if (atomize) { - item = FnData.fnDataItem(item); // NOPMD - intentional - - // if (IUntypedAtomicItem.class.isInstance(item)) { // NOPMD - // // TODO: apply cast to atomic type - // } - - // promote URIs to strings if a string is required - if (IStringItem.class.equals(requiredSequenceTypeClass) && IAnyUriItem.class.isInstance(item)) { - item = IStringItem.cast((IAnyUriItem) item); // NOPMD - intentional - } - } + ISequenceType requiredSequenceType = argument.getSequenceType(); + Class requiredSequenceTypeClass = requiredSequenceType.getType(); - // item = requiredSequenceType. - if (!requiredSequenceTypeClass.isInstance(item)) { - throw new InvalidTypeMetapathException( - item, - String.format("The type '%s' is not a subtype of '%s'", item.getClass().getName(), - requiredSequenceTypeClass.getName())); - } - result.add(item); + Stream stream = sequence.safeStream(); + + if (IAnyAtomicItem.class.isAssignableFrom(requiredSequenceTypeClass)) { + Stream atomicStream = stream.flatMap(FnData::atomize); + + // if (IUntypedAtomicItem.class.isInstance(item)) { // NOPMD + // // TODO: apply cast to atomic type + // } + + if (IStringItem.class.equals(requiredSequenceTypeClass)) { + // promote URIs to strings if a string is required + atomicStream = atomicStream.map(item -> IAnyUriItem.class.isInstance(item) ? IStringItem.cast(item) : item); } - retval = ISequence.of(result); + + stream = atomicStream; } - return retval; + + stream = stream.peek(item -> { + if (!requiredSequenceTypeClass.isInstance(item)) { + throw new InvalidTypeMetapathException( + item, + String.format("The type '%s' is not a subtype of '%s'", + item.getClass().getName(), + requiredSequenceTypeClass.getName())); + } + }); + assert stream != null; + + return ISequence.of(stream); } @Override @@ -321,7 +285,7 @@ public ISequence execute( try { List> convertedArguments = convertArguments(this, arguments); - IItem contextItem = isFocusDepenent() ? FunctionUtils.requireFirstItem(focus, true) : null; + IItem contextItem = isFocusDepenent() ? ObjectUtils.requireNonNull(focus.getFirstItem(true)) : null; CallingContext callingContext = null; ISequence result = null; @@ -381,32 +345,6 @@ public String toString() { return toSignature(); } - @Override - public String toSignature() { - StringBuilder builder = new StringBuilder() - .append("Q{") - .append(getNamespace()) - .append('}') - .append(getName()) // name - .append('('); // arguments - - List arguments = getArguments(); - if (arguments.isEmpty()) { - builder.append("()"); - } else { - builder.append(arguments.stream().map(argument -> argument.toSignature()).collect(Collectors.joining(","))); - - if (isArityUnbounded()) { - builder.append(", ..."); - } - } - - builder.append(") as ") - .append(getResult().toSignature());// return type - - return builder.toString(); - } - public final class CallingContext { @Nullable private final IItem contextItem; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java index cef0f0aa3..9f35470c0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/DocumentFunctionException.java @@ -104,7 +104,7 @@ public DocumentFunctionException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FODC"; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionLibrary.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionLibrary.java index 739059485..5e0c4463f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionLibrary.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionLibrary.java @@ -53,7 +53,7 @@ public class FunctionLibrary implements IFunctionLibrary { * if the provided function has the same arity as a previously * registered function with the same name */ - public void registerFunction(@NonNull IFunction function) { + public final void registerFunction(@NonNull IFunction function) { registerFunctionByQName(function); registerFunctionByName(function); } @@ -89,12 +89,9 @@ private void registerFunctionByName(@NonNull IFunction function) { } @Override - public Stream getFunctionsAsStream() { + public Stream stream() { synchronized (this) { - return ObjectUtils.notNull( - libraryByQName.values().stream().flatMap(set -> { - return set.getFunctionsAsStream(); - })); + return ObjectUtils.notNull(libraryByQName.values().stream().flatMap(NamedFunctionSet::getFunctionsAsStream)); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionService.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionService.java index 07c94382e..409b0d11f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionService.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionService.java @@ -31,6 +31,7 @@ import java.util.ServiceLoader; import java.util.ServiceLoader.Provider; +import java.util.stream.Stream; import javax.xml.namespace.QName; @@ -42,7 +43,7 @@ public final class FunctionService { @NonNull private final ServiceLoader loader; @NonNull - private IFunctionLibrary library; + private final IFunctionLibrary library; /** * Get the singleton instance of the function service. @@ -59,30 +60,14 @@ public static FunctionService getInstance() { @SuppressWarnings("null") public FunctionService() { this.loader = ServiceLoader.load(IFunctionLibrary.class); - this.library = load(); - } - - /** - * Load all known functions registered with this function service. - * - * @return the function library - */ - @NonNull - public IFunctionLibrary load() { ServiceLoader loader = getLoader(); FunctionLibrary functionLibrary = new FunctionLibrary(); loader.stream() .map(Provider::get) - .flatMap(library -> { - return library.getFunctionsAsStream(); - }) + .flatMap(IFunctionLibrary::stream) .forEachOrdered(function -> functionLibrary.registerFunction(ObjectUtils.notNull(function))); - - synchronized (this) { - this.library = functionLibrary; - } - return functionLibrary; + this.library = functionLibrary; } /** @@ -95,6 +80,15 @@ private ServiceLoader getLoader() { return loader; } + /** + * Retrieve the collection of function signatures in this library as a stream. + * + * @return a stream of function signatures + */ + public Stream stream() { + return this.library.stream(); + } + /** * Retrieve the function with the provided name that supports the signature of * the provided methods, if such a function exists. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java index 3a9790afe..73d7dca75 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionUtils.java @@ -112,74 +112,6 @@ public static long asLong(@NonNull BigInteger value) { return value.longValueExact(); } - /** - * Retrieves the first item in a sequence. If the sequence is empty, a - * {@link TypeMetapathException} exception is thrown. If requireSingleton is - * {@code true} and the sequence contains more than one item, a - * {@link TypeMetapathException} is thrown. - * - * @param - * the item type to return derived from the provided sequence - * @param sequence - * the sequence to retrieve the first item from - * @param requireSingleton - * if {@code true} then a {@link TypeMetapathException} is thrown if - * the sequence contains more than one item - * @return {@code null} if the sequence is empty, or the item otherwise - * @throws TypeMetapathException - * if the sequence is empty, or contains more than one item and - * requireSingleton is {@code true} - */ - @NonNull - public static ITEM requireFirstItem(@NonNull ISequence sequence, - boolean requireSingleton) { - if (sequence.isEmpty()) { - throw new InvalidTypeMetapathException( - null, - "Expected a non-empty sequence, but sequence was empty."); - } - List items = sequence.asList(); - if (requireSingleton && items.size() != 1) { - throw new InvalidTypeMetapathException( - null, - String.format("sequence expected to contain one item, but found '%d'", items.size())); - } - return ObjectUtils.notNull(items.iterator().next()); - } - - /** - * Retrieves the first item in a sequence. If the sequence is empty, a - * {@code null} result is returned. If requireSingleton is {@code true} and the - * sequence contains more than one item, a {@link TypeMetapathException} is - * thrown. - * - * @param - * the item type to return derived from the provided sequence - * @param sequence - * the sequence to retrieve the first item from - * @param requireSingleton - * if {@code true} then a {@link TypeMetapathException} is thrown if - * the sequence contains more than one item - * @return {@code null} if the sequence is empty, or the item otherwise - * @throws TypeMetapathException - * if the sequence contains more than one item and requireSingleton is - * {@code true} - */ - @Nullable - public static ITEM getFirstItem(@NonNull ISequence sequence, boolean requireSingleton) { - @Nullable ITEM retval = null; - if (!sequence.isEmpty()) { - List items = sequence.asList(); - if (requireSingleton && items.size() != 1) { - throw new InvalidTypeMetapathException( - null, - String.format("sequence expected to contain one item, but found '%d'", items.size())); - } - retval = items.iterator().next(); - } - return retval; - } - /** * Gets the first item of the provided sequence as a {@link INumericItem} value. * If the sequence is empty, then a {@code null} value is returned. @@ -198,7 +130,7 @@ public static ITEM getFirstItem(@NonNull ISequence se */ @Nullable public static INumericItem toNumeric(@NonNull ISequence sequence, boolean requireSingleton) { - IItem item = getFirstItem(sequence, requireSingleton); + IItem item = sequence.getFirstItem(requireSingleton); return item == null ? null : toNumeric(item); } @@ -215,7 +147,7 @@ public static INumericItem toNumeric(@NonNull ISequence sequence, boolean req @NonNull public static INumericItem toNumeric(@NonNull IItem item) { // atomize - IAnyAtomicItem atomicItem = FnData.fnDataItem(item); + IAnyAtomicItem atomicItem = ISequence.getFirstItem(FnData.atomize(item), true); return toNumeric(atomicItem); } @@ -229,7 +161,7 @@ public static INumericItem toNumeric(@NonNull IItem item) { * if the item cannot be cast to a numeric value */ @NonNull - public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) { + public static INumericItem toNumeric(@Nullable IAnyAtomicItem item) { try { return IDecimalItem.cast(item); } catch (InvalidValueForCastFunctionException ex) { @@ -373,7 +305,7 @@ public static TYPE requireTypeOrNull(Class clazz, @Nu */ @NonNull public static Stream> getTypes(@NonNull Stream items) { - return ObjectUtils.notNull(items.map(item -> item.getClass())); + return ObjectUtils.notNull(items.map(Object::getClass)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IArgument.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IArgument.java index 6012b557f..451323f9b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IArgument.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IArgument.java @@ -76,8 +76,9 @@ static Builder builder() { */ final class Builder { private String name; + @NonNull private Class type = IItem.class; - private Occurrence occurrence = Occurrence.ONE; + private Occurrence occurrence; private Builder() { // construct a new non-initialized builder @@ -177,7 +178,7 @@ private Builder occurrence(@NonNull Occurrence occurrence) { public IArgument build() { return new ArgumentImpl( ObjectUtils.requireNonNull(name, "the argument name must not be null"), - new SequenceTypeImpl(type, occurrence)); + ISequenceType.of(type, ObjectUtils.requireNonNull(occurrence, "occurrence"))); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunction.java index ec45095dd..80db61094 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunction.java @@ -39,6 +39,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.xml.namespace.QName; @@ -91,7 +92,9 @@ enum FunctionProperty { * @return the function's name */ @NonNull - String getName(); + default String getName() { + return ObjectUtils.notNull(getQName().getLocalPart()); + } /** * Retrieve the namespace of the function. @@ -99,7 +102,9 @@ enum FunctionProperty { * @return the function's namespace */ @NonNull - String getNamespace(); + default String getNamespace() { + return ObjectUtils.notNull(getQName().getNamespaceURI()); + } /** * Retrieve the namespace qualified name of the function. @@ -107,9 +112,7 @@ enum FunctionProperty { * @return the namespace qualified name */ @NonNull - default QName getQName() { - return new QName(getNamespace(), getName()); - } + QName getQName(); /** * Retrieve the set of assigned function properties. @@ -223,7 +226,16 @@ ISequence execute( * * @return the signature */ - String toSignature(); + @NonNull + default String toSignature() { + return ObjectUtils.notNull(String.format("Q{%s}%s(%s) as %s", + getNamespace(), + getName(), + getArguments().isEmpty() ? "" + : getArguments().stream().map(IArgument::toSignature).collect(Collectors.joining(",")) + + (isArityUnbounded() ? ", ..." : ""), + getResult().toSignature())); + } /** * Construct a new function signature builder. @@ -247,7 +259,9 @@ final class Builder { private final EnumSet properties = EnumSet.noneOf(FunctionProperty.class); @NonNull private final List arguments = new LinkedList<>(); + @NonNull private Class returnType = IItem.class; + @NonNull private Occurrence returnOccurrence = Occurrence.ONE; private IFunctionExecutor functionHandler; @@ -500,15 +514,6 @@ public Builder functionHandler(@NonNull IFunctionExecutor handler) { */ @NonNull public IFunction build() { - ISequenceType sequenceType; - if (returnType == null) { - sequenceType = ISequenceType.EMPTY; - } else { - sequenceType = new SequenceTypeImpl( - returnType, - ObjectUtils.requireNonNull(returnOccurrence, "the return occurrence must not be null")); - } - if (properties.contains(FunctionProperty.UNBOUNDED_ARITY) && arguments.isEmpty()) { throw new IllegalStateException("to allow unbounded arity, at least one argument must be provided"); } @@ -518,7 +523,7 @@ public IFunction build() { ObjectUtils.requireNonNull(namespace, "the namespace must not be null"), properties, new ArrayList<>(arguments), - sequenceType, + ISequenceType.of(returnType, returnOccurrence), ObjectUtils.requireNonNull(functionHandler, "the function handler must not be null")); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunctionLibrary.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunctionLibrary.java index 393e9c36f..12616701a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunctionLibrary.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunctionLibrary.java @@ -40,7 +40,7 @@ public interface IFunctionLibrary { * @return a stream of function signatures */ @NonNull - Stream getFunctionsAsStream(); + Stream stream(); /** * Determine if there is a function with the provided name that supports the diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ISequenceType.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ISequenceType.java index dbb26a80e..cee125556 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ISequenceType.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/ISequenceType.java @@ -45,7 +45,7 @@ public Class getType() { @Override public Occurrence getOccurrence() { - return null; + return Occurrence.ZERO; } @Override @@ -54,6 +54,23 @@ public String toSignature() { } }; + /** + * Create new sequence type using the provide type and occurrence. + * + * @param type + * the sequence item type + * @param occurrence + * the expected occurrence of the sequence + * @return the new sequence type + */ + @SuppressWarnings("PMD.ShortMethodName") + @NonNull + static ISequenceType of(@NonNull Class type, @NonNull Occurrence occurrence) { + return Occurrence.ZERO.equals(occurrence) + ? EMPTY + : new SequenceTypeImpl(type, occurrence); + } + /** * Determine if the sequence is empty (if it holds any data) or not. * @@ -71,8 +88,8 @@ public String toSignature() { /** * Get the occurrence of the sequence. * - * @return the occurrence of the sequence or {@code null} if the sequence is - * empty + * @return the occurrence of the sequence or {@code Occurrence#ZERO} if the + * sequence is empty */ Occurrence getOccurrence(); @@ -81,5 +98,6 @@ public String toSignature() { * * @return the signature */ + @NonNull String toSignature(); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java index b3504b28a..37b62a3b7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidArgumentFunctionException.java @@ -103,7 +103,7 @@ public InvalidArgumentFunctionException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FORG"; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidTypeFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidTypeFunctionException.java index 9fe744ca9..05b78569a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidTypeFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidTypeFunctionException.java @@ -95,7 +95,7 @@ private static String generateMessage(@NonNull IItem item) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FOTY"; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidValueForCastFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidValueForCastFunctionException.java index 0750128fc..183640e9b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidValueForCastFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/InvalidValueForCastFunctionException.java @@ -41,7 +41,7 @@ public class InvalidValueForCastFunctionException * the exception message */ public InvalidValueForCastFunctionException(String message) { - super(InvalidArgumentFunctionException.INVALID_VALUE_FOR_CAST, message); + super(INVALID_VALUE_FOR_CAST, message); } /** @@ -54,7 +54,7 @@ public InvalidValueForCastFunctionException(String message) { * the original exception cause */ public InvalidValueForCastFunctionException(String message, Throwable cause) { - super(InvalidArgumentFunctionException.INVALID_VALUE_FOR_CAST, message, cause); + super(INVALID_VALUE_FOR_CAST, message, cause); } /** @@ -64,7 +64,7 @@ public InvalidValueForCastFunctionException(String message, Throwable cause) { * the original exception cause */ public InvalidValueForCastFunctionException(Throwable cause) { - super(InvalidArgumentFunctionException.INVALID_VALUE_FOR_CAST, cause); + super(INVALID_VALUE_FOR_CAST, cause); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java new file mode 100644 index 000000000..78af9739b --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/JsonFunctionException.java @@ -0,0 +1,98 @@ +/* + * 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; + +import gov.nist.secauto.metaschema.core.metapath.AbstractCodedMetapathException; + +public class JsonFunctionException + extends AbstractCodedMetapathException { + /** + * err:FOJS0003: + * This error is raised if the input contains duplicate keys, when the chosen + * policy is to reject duplicates. + */ + public static final int DUPLICATE_KEYS = 3; + + /** + * err:FOJS0005: + * This error is raised if the $options map contains an invalid entry. + */ + public static final int INVALID_OPTION = 5; + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and no cause. + * + * @param code + * the error code value + * @param message + * the exception message + */ + public JsonFunctionException(int code, String message) { + super(code, message); + } + + /** + * Constructs a new exception with the provided {@code code}, {@code message}, + * and {@code cause}. + * + * @param code + * the error code value + * @param message + * the exception message + * @param cause + * the original exception cause + */ + public JsonFunctionException(int code, String message, Throwable cause) { + super(code, message, cause); + } + + /** + * Constructs a new exception with the provided {@code code}, no message, and + * the {@code cause}. + * + * @param code + * the error code value + * @param cause + * the original exception cause + */ + public JsonFunctionException(int code, Throwable cause) { + super(code, cause); + } + + @Override + public String getCodePrefix() { + return "FOJS"; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/Occurrence.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/Occurrence.java index 95fde71dc..617fec1b7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/Occurrence.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/Occurrence.java @@ -26,8 +26,14 @@ package gov.nist.secauto.metaschema.core.metapath.function; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; + import java.util.Objects; +import edu.umd.cs.findbugs.annotations.NonNull; + /** * Identifies the occurrence of a sequence used a function argument or return * value. @@ -36,31 +42,35 @@ public enum Occurrence { /** * An empty sequence. */ - ZERO("", true), + ZERO("", true, Occurrence::handleZero), /** * The occurrence indicator {@code "?"}. */ - ZERO_OR_ONE("?", true), + ZERO_OR_ONE("?", true, Occurrence::handleZeroOrOne), /** * No occurrence indicator. */ - ONE("", false), + ONE("", false, Occurrence::handleOne), /** * The occurrence indicator {@code "*"}. */ - ZERO_OR_MORE("*", true), + ZERO_OR_MORE("*", true, Occurrence::handleZeroOrMore), /** * The occurrence indicator {@code "+"}. */ - ONE_OR_MORE("+", false); + ONE_OR_MORE("+", false, Occurrence::handleOneOrMore); + @NonNull private final String indicator; private final boolean optional; + @NonNull + private final ISequenceHandler sequenceHandler; - Occurrence(String indicator, boolean optional) { + Occurrence(@NonNull String indicator, boolean optional, @NonNull ISequenceHandler sequenceHandler) { Objects.requireNonNull(indicator, "indicator"); this.indicator = indicator; this.optional = optional; + this.sequenceHandler = sequenceHandler; } /** @@ -68,6 +78,7 @@ public enum Occurrence { * * @return the occurrence indicator */ + @NonNull public String getIndicator() { return indicator; } @@ -81,4 +92,88 @@ public String getIndicator() { public boolean isOptional() { return optional; } + + /** + * Get the handler used to check that a sequence meets the occurrence + * requirement. + * + * @return the handler + */ + @NonNull + public ISequenceHandler getSequenceHandler() { + return sequenceHandler; + } + + @NonNull + private static ISequence handleZero(@NonNull ISequence sequence) { + int size = sequence.size(); + if (size != 0) { + throw new InvalidTypeMetapathException( + null, + String.format("an empty sequence expected, but size is '%d'", size)); + } + return ISequence.empty(); + } + + @NonNull + private static ISequence handleOne(@NonNull ISequence sequence) { + int size = sequence.size(); + if (size != 1) { + throw new InvalidTypeMetapathException( + null, + String.format("a sequence of one expected, but size is '%d'", size)); + } + + T item = sequence.getFirstItem(true); + return item == null ? ISequence.empty() : ISequence.of(item); + } + + @NonNull + private static ISequence handleZeroOrOne(@NonNull ISequence sequence) { + int size = sequence.size(); + if (size > 1) { + throw new InvalidTypeMetapathException( + null, + String.format("a sequence of zero or one expected, but size is '%d'", size)); + } + + T item = sequence.getFirstItem(false); + return item == null ? ISequence.empty() : ISequence.of(item); + } + + @NonNull + private static ISequence handleZeroOrMore(@NonNull ISequence sequence) { + return sequence; + } + + @NonNull + private static ISequence handleOneOrMore(@NonNull ISequence sequence) { + int size = sequence.size(); + if (size < 1) { + throw new InvalidTypeMetapathException( + null, + String.format("a sequence of one or more expected, but size is '%d'", size)); + } + return sequence; + } + + @FunctionalInterface + public interface ISequenceHandler { + /** + * Check the provided sequence matches the occurrence. + *

+ * This method may return a new sequence that more efficiently addresses the + * occurrence. + * + * @param + * the sequence item Java type + * @param sequence + * the sequence to check occurrence for + * @return the sequence + * @throws InvalidTypeMetapathException + * if the sequence doesn't match the required occurrence + */ + @NonNull + ISequence handle(@NonNull ISequence sequence); + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/OperationFunctions.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/OperationFunctions.java index 018a10512..2a8078eb8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/OperationFunctions.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/OperationFunctions.java @@ -62,14 +62,36 @@ private OperationFunctions() { // disable } + /** + * Based on XPath 3.1 op:add-yearMonthDuration-to-date. + * + * @param instant + * a point in time + * @param duration + * the duration to add + * @return the result of adding the duration to the date + */ @NonNull - public static IDateItem opAddYearMonthDurationToDate(@NonNull IDateItem arg1, @NonNull IYearMonthDurationItem arg2) { - return addDurationToDate(arg1.asZonedDateTime(), arg2.asPeriod()); + public static IDateItem opAddYearMonthDurationToDate(@NonNull IDateItem instant, + @NonNull IYearMonthDurationItem duration) { + return addDurationToDate(instant.asZonedDateTime(), duration.asPeriod()); } + /** + * Based on XPath 3.1 op:add-dayTimeDuration-to-date. + * + * @param instant + * a point in time + * @param duration + * the duration to add + * @return the result of adding the duration to the date + */ @NonNull - public static IDateItem opAddDayTimeDurationToDate(@NonNull IDateItem arg1, @NonNull IDayTimeDurationItem arg2) { - return addDurationToDate(arg1.asZonedDateTime(), arg2.asDuration()); + public static IDateItem opAddDayTimeDurationToDate(@NonNull IDateItem instant, + @NonNull IDayTimeDurationItem duration) { + return addDurationToDate(instant.asZonedDateTime(), duration.asDuration()); } @NonNull @@ -84,6 +106,16 @@ private static IDateItem addDurationToDate(@NonNull ZonedDateTime dateTime, @Non return IDateItem.valueOf(result); } + /** + * Based on XPath 3.1 op:add-yearMonthDurations. + * + * @param arg1 + * the first duration + * @param arg2 + * the second duration + * @return the sum of two duration values + */ @NonNull public static IYearMonthDurationItem opAddYearMonthDurations( @NonNull IYearMonthDurationItem arg1, @@ -101,6 +133,16 @@ public static IYearMonthDurationItem opAddYearMonthDurations( return IYearMonthDurationItem.valueOf(result); } + /** + * Based on XPath 3.1 op:add-dayTimeDurations. + * + * @param arg1 + * the first duration + * @param arg2 + * the second duration + * @return the sum of two duration values + */ @NonNull public static IDayTimeDurationItem opAddDayTimeDurations( @NonNull IDayTimeDurationItem arg1, @@ -118,13 +160,23 @@ public static IDayTimeDurationItem opAddDayTimeDurations( return IDayTimeDurationItem.valueOf(result); } + /** + * Based on XPath 3.1 op:add-yearMonthDuration-to-dateTime. + * + * @param instant + * a point in time + * @param duration + * the duration to add + * @return the result of adding the duration to the date + */ @NonNull public static IDateTimeItem opAddYearMonthDurationToDateTime( - @NonNull IDateTimeItem arg1, - @NonNull IYearMonthDurationItem arg2) { + @NonNull IDateTimeItem instant, + @NonNull IYearMonthDurationItem duration) { ZonedDateTime result; try { - result = arg1.asZonedDateTime().plus(arg2.asPeriod()); + result = instant.asZonedDateTime().plus(duration.asPeriod()); } catch (ArithmeticException ex) { throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); } @@ -132,13 +184,23 @@ public static IDateTimeItem opAddYearMonthDurationToDateTime( return IDateTimeItem.valueOf(result); } + /** + * Based on XPath 3.1 op:add-dayTimeDuration-to-dateTime. + * + * @param instant + * a point in time + * @param duration + * the duration to add + * @return the result of adding the duration to the date + */ @NonNull public static IDateTimeItem opAddDayTimeDurationToDateTime( - @NonNull IDateTimeItem arg1, - @NonNull IDayTimeDurationItem arg2) { + @NonNull IDateTimeItem instant, + @NonNull IDayTimeDurationItem duration) { ZonedDateTime result; try { - result = arg1.asZonedDateTime().plus(arg2.asDuration()); + result = instant.asZonedDateTime().plus(duration.asDuration()); } catch (ArithmeticException ex) { throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, ex); } @@ -146,23 +208,53 @@ public static IDateTimeItem opAddDayTimeDurationToDateTime( return IDateTimeItem.valueOf(result); } + /** + * Based on XPath 3.1 op:subtract-dates. + * + * @param date1 + * the first point in time + * @param date2 + * the second point in time + * @return the elapsed time between the starting instant and ending instant + */ @NonNull - public static IDayTimeDurationItem opSubtractDates(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { - return between(arg1.asZonedDateTime(), arg2.asZonedDateTime()); + public static IDayTimeDurationItem opSubtractDates(@NonNull IDateItem date1, @NonNull IDateItem date2) { + return between(date1.asZonedDateTime(), date2.asZonedDateTime()); } + /** + * Based on XPath 3.1 op:subtract-yearMonthDuration-from-date. + * + * @param date + * a point in time + * @param duration + * the duration to subtract + * @return the result of subtracting the duration from the date + */ @NonNull public static IDateItem opSubtractYearMonthDurationFromDate( - @NonNull IDateItem arg1, - @NonNull IYearMonthDurationItem arg2) { - return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.asPeriod()); + @NonNull IDateItem date, + @NonNull IYearMonthDurationItem duration) { + return subtractDurationFromDate(date.asZonedDateTime(), duration.asPeriod()); } + /** + * Based on XPath 3.1 op:subtract-dayTimeDuration-from-date. + * + * @param date + * a point in time + * @param duration + * the duration to subtract + * @return the result of subtracting the duration from the date + */ @NonNull public static IDateItem opSubtractDayTimeDurationFromDate( - @NonNull IDateItem arg1, - @NonNull IDayTimeDurationItem arg2) { - return subtractDurationFromDate(arg1.asZonedDateTime(), arg2.asDuration()); + @NonNull IDateItem date, + @NonNull IDayTimeDurationItem duration) { + return subtractDurationFromDate(date.asZonedDateTime(), duration.asDuration()); } @NonNull @@ -174,6 +266,16 @@ private static IDateItem subtractDurationFromDate( return IDateItem.valueOf(result); } + /** + * Based on XPath 3.1 op:subtract-yearMonthDurations. + * + * @param arg1 + * the first duration + * @param arg2 + * the second duration + * @return the result of subtracting the second duration from the first + */ @NonNull public static IYearMonthDurationItem opSubtractYearMonthDurations( @NonNull IYearMonthDurationItem arg1, @@ -186,6 +288,16 @@ public static IYearMonthDurationItem opSubtractYearMonthDurations( return IYearMonthDurationItem.valueOf(duration); } + /** + * Based on XPath 3.1 op:subtract-dayTimeDurations. + * + * @param arg1 + * the first duration + * @param arg2 + * the second duration + * @return the result of subtracting the second duration from the first + */ @NonNull public static IDayTimeDurationItem opSubtractDayTimeDurations( @NonNull IDayTimeDurationItem arg1, @@ -198,11 +310,31 @@ public static IDayTimeDurationItem opSubtractDayTimeDurations( return IDayTimeDurationItem.valueOf(duration); } + /** + * Based on XPath 3.1 op:subtract-dateTimes. + * + * @param time1 + * the first point in time + * @param time2 + * the second point in time + * @return the duration the occurred between the two points in time + */ @NonNull - public static IDayTimeDurationItem opSubtractDateTimes(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { - return between(arg1.asZonedDateTime(), arg2.asZonedDateTime()); + public static IDayTimeDurationItem opSubtractDateTimes(@NonNull IDateTimeItem time1, @NonNull IDateTimeItem time2) { + return between(time1.asZonedDateTime(), time2.asZonedDateTime()); } + /** + * Based on XPath 3.1 op:subtract-dateTimes. + * + * @param time1 + * the first point in time + * @param time2 + * the second point in time + * @return the duration the occurred between the two points in time + */ @NonNull private static IDayTimeDurationItem between(@NonNull ZonedDateTime time1, @NonNull ZonedDateTime time2) { @SuppressWarnings("null") @@ -210,25 +342,56 @@ private static IDayTimeDurationItem between(@NonNull ZonedDateTime time1, @NonNu return IDayTimeDurationItem.valueOf(between); } + /** + * Based on XPath 3.1 op:subtract-yearMonthDuration-from-dateTime. + * + * @param moment + * a point in time + * @param duration + * the duration to subtract + * @return the result of subtracting the duration from a point in time + */ @NonNull public static IDateTimeItem opSubtractYearMonthDurationFromDateTime( - @NonNull IDateTimeItem arg1, - @NonNull IYearMonthDurationItem arg2) { + @NonNull IDateTimeItem moment, + @NonNull IYearMonthDurationItem duration) { @SuppressWarnings("null") - @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().minus(arg2.asPeriod()); + @NonNull ZonedDateTime dateTime = moment.asZonedDateTime().minus(duration.asPeriod()); return IDateTimeItem.valueOf(dateTime); } + /** + * Based on XPath 3.1 op:subtract-dayTimeDuration-from-dateTime. + * + * @param moment + * a point in time + * @param duration + * the duration to subtract + * @return the result of subtracting the duration from a point in time + */ @NonNull public static IDateTimeItem opSubtractDayTimeDurationFromDateTime( - @NonNull IDateTimeItem arg1, - @NonNull IDayTimeDurationItem arg2) { + @NonNull IDateTimeItem moment, + @NonNull IDayTimeDurationItem duration) { @SuppressWarnings("null") - @NonNull ZonedDateTime dateTime = arg1.asZonedDateTime().plus(arg2.asDuration()); + @NonNull ZonedDateTime dateTime = moment.asZonedDateTime().plus(duration.asDuration()); return IDateTimeItem.valueOf(dateTime); } + /** + * Based on XPath 3.1 op:multiply-yearMonthDuration. + * + * @param arg1 + * the duration value + * @param arg2 + * the number to multiply by + * @return the result of multiplying a {@link IYearMonthDurationItem} by a + * number + */ @NonNull public static IYearMonthDurationItem opMultiplyYearMonthDuration( @NonNull IYearMonthDurationItem arg1, @@ -245,6 +408,16 @@ public static IYearMonthDurationItem opMultiplyYearMonthDuration( return IYearMonthDurationItem.valueOf(period); } + /** + * Based on XPath 3.1 op:multiply-dayTimeDuration. + * + * @param arg1 + * the duration value + * @param arg2 + * the number to multiply by + * @return the result of multiplying a {@link IDayTimeDurationItem} by a number + */ @NonNull public static IDayTimeDurationItem opMultiplyDayTimeDuration( @NonNull IDayTimeDurationItem arg1, @@ -261,6 +434,16 @@ public static IDayTimeDurationItem opMultiplyDayTimeDuration( return IDayTimeDurationItem.valueOf(duration); } + /** + * Based on XPath 3.1 op:divide-yearMonthDuration. + * + * @param arg1 + * the duration value + * @param arg2 + * the number to divide by + * @return the result of dividing a {@link IYearMonthDurationItem} by a number + */ @NonNull public static IYearMonthDurationItem opDivideYearMonthDuration( @NonNull IYearMonthDurationItem arg1, @@ -279,6 +462,16 @@ public static IYearMonthDurationItem opDivideYearMonthDuration( return IYearMonthDurationItem.valueOf(years, months); } + /** + * Based on XPath 3.1 op:divide-dayTimeDuration. + * + * @param arg1 + * the duration value + * @param arg2 + * the number to divide by + * @return the result of dividing a {@link IDayTimeDurationItem} by a number + */ @NonNull public static IDayTimeDurationItem opDivideDayTimeDuration( @NonNull IDayTimeDurationItem arg1, @@ -286,13 +479,23 @@ public static IDayTimeDurationItem opDivideDayTimeDuration( try { @SuppressWarnings("null") @NonNull Duration duration = arg1.asDuration().dividedBy(FunctionUtils.asLong(arg2.round())); - return IDayTimeDurationItem - .valueOf(duration); + return IDayTimeDurationItem.valueOf(duration); } catch (ArithmeticException ex) { throw new ArithmeticFunctionException(ArithmeticFunctionException.DIVISION_BY_ZERO, "Division by zero", ex); } } + /** + * Based on XPath 3.1 op:divide-dayTimeDuration-by-dayTimeDuration. + * + * @param arg1 + * the first duration value + * @param arg2 + * the second duration value + * @return the ratio of two {@link IDayTimeDurationItem} values, as a decimal + * number + */ @NonNull public static IDecimalItem opDivideDayTimeDurationByDayTimeDuration( @NonNull IDayTimeDurationItem arg1, @@ -303,38 +506,115 @@ public static IDecimalItem opDivideDayTimeDurationByDayTimeDuration( IDecimalItem.valueOf(arg2.asSeconds()))); } + /** + * Based on XPath 3.1 op:date-equal. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is the same instant in time as the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateEqual(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { // TODO: avoid cast? return opDateTimeEqual(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); } + /** + * Based on XPath 3.1 op:dateTime-equal. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is the same instant in time as the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateTimeEqual(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { return IBooleanItem.valueOf(arg1.asZonedDateTime().equals(arg2.asZonedDateTime())); } + /** + * Based on XPath 3.1 op:duration-equal. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is the same duration as the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDurationEqual(@NonNull IDurationItem arg1, @NonNull IDurationItem arg2) { return IBooleanItem.valueOf(arg1.compareTo(arg2) == 0); } + /** + * Based on XPath 3.1 op:base64Binary-equal. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is equal to the second, or + * {@code false} otherwise + */ @NonNull public static IBooleanItem opBase64BinaryEqual(@NonNull IBase64BinaryItem arg1, @NonNull IBase64BinaryItem arg2) { return IBooleanItem.valueOf(arg1.asByteBuffer().equals(arg2.asByteBuffer())); } + /** + * Based on XPath 3.1 op:date-greater-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a later instant in time than + * the second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateGreaterThan(@NonNull IDateItem arg1, @NonNull IDateItem arg2) { // TODO: avoid cast? return opDateTimeGreaterThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); } + /** + * Based on XPath 3.1 op:dateTime-greater-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a later instant in time than + * the second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateTimeGreaterThan(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) { return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) > 0); } + /** + * Based on XPath 3.1 op:yearMonthDuration-greater-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a longer duration than the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opYearMonthDurationGreaterThan( @NonNull IYearMonthDurationItem arg1, @@ -346,6 +626,17 @@ public static IBooleanItem opYearMonthDurationGreaterThan( return IBooleanItem.valueOf(p1.toTotalMonths() > p2.toTotalMonths()); } + /** + * Based on XPath 3.1 op:dayTimeDuration-greater-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a longer duration than the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDayTimeDurationGreaterThan( @NonNull IDayTimeDurationItem arg1, @@ -353,6 +644,17 @@ public static IBooleanItem opDayTimeDurationGreaterThan( return IBooleanItem.valueOf(arg1.compareTo(arg2) > 0); } + /** + * Based on XPath 3.1 op:base64Binary-greater-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is greater than the second, or + * {@code false} otherwise + */ @NonNull public static IBooleanItem opBase64BinaryGreaterThan( @NonNull IBase64BinaryItem arg1, @@ -360,6 +662,17 @@ public static IBooleanItem opBase64BinaryGreaterThan( return IBooleanItem.valueOf(arg1.compareTo(arg2) > 0); } + /** + * Based on XPath 3.1 op:date-less-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is an earlier instant in time than + * the second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateLessThan( @NonNull IDateItem arg1, @@ -367,6 +680,17 @@ public static IBooleanItem opDateLessThan( return opDateTimeLessThan(IDateTimeItem.cast(arg1), IDateTimeItem.cast(arg2)); } + /** + * Based on XPath 3.1 op:dateTime-less-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is an earlier instant in time than + * the second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDateTimeLessThan( @NonNull IDateTimeItem arg1, @@ -374,6 +698,17 @@ public static IBooleanItem opDateTimeLessThan( return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) < 0); } + /** + * Based on XPath 3.1 op:yearMonthDuration-less-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a shorter duration than the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opYearMonthDurationLessThan(@NonNull IYearMonthDurationItem arg1, @NonNull IYearMonthDurationItem arg2) { @@ -384,6 +719,17 @@ public static IBooleanItem opYearMonthDurationLessThan(@NonNull IYearMonthDurati return IBooleanItem.valueOf(p1.toTotalMonths() < p2.toTotalMonths()); } + /** + * Based on XPath 3.1 op:dayTimeDuration-less-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is a shorter duration than the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opDayTimeDurationLessThan( @NonNull IDayTimeDurationItem arg1, @@ -391,6 +737,17 @@ public static IBooleanItem opDayTimeDurationLessThan( return IBooleanItem.valueOf(arg1.compareTo(arg2) < 0); } + /** + * Based on XPath 3.1 op:base64Binary-less-than. + * + * @param arg1 + * the first value + * @param arg2 + * the second value + * @return {@code true} if the first argument is less than the second, or + * {@code false} otherwise + */ @NonNull public static IBooleanItem opBase64BinaryLessThan( @NonNull IBase64BinaryItem arg1, @@ -398,6 +755,16 @@ public static IBooleanItem opBase64BinaryLessThan( return IBooleanItem.valueOf(arg1.compareTo(arg2) < 0); } + /** + * Based on XPath 3.1 op:numeric-add. + * + * @param left + * the first number + * @param right + * the second number + * @return the result of adding the second number to the first number + */ @NonNull public static INumericItem opNumericAdd(@NonNull INumericItem left, @NonNull INumericItem right) { INumericItem retval; @@ -421,6 +788,16 @@ public static INumericItem opNumericAdd(@NonNull INumericItem left, @NonNull INu return retval; } + /** + * Based on XPath 3.1 op:numeric-subtract. + * + * @param left + * the first number + * @param right + * the second number + * @return the result of subtracting the second number from the first number + */ @NonNull public static INumericItem opNumericSubtract(@NonNull INumericItem left, @NonNull INumericItem right) { INumericItem retval; @@ -444,6 +821,16 @@ public static INumericItem opNumericSubtract(@NonNull INumericItem left, @NonNul return retval; } + /** + * Based on XPath 3.1 op:numeric-multiply. + * + * @param left + * the first number + * @param right + * the second number + * @return the result of multiplying the first number by the second number + */ @NonNull public static INumericItem opNumericMultiply(@NonNull INumericItem left, @NonNull INumericItem right) { INumericItem retval; @@ -464,6 +851,16 @@ public static INumericItem opNumericMultiply(@NonNull INumericItem left, @NonNul return retval; } + /** + * Based on XPath 3.1 op:numeric-divide. + * + * @param dividend + * the number to be divided + * @param divisor + * the number to divide by + * @return the quotient + */ @NonNull public static IDecimalItem opNumericDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) { // create a decimal result @@ -481,6 +878,16 @@ public static IDecimalItem opNumericDivide(@NonNull INumericItem dividend, @NonN return IDecimalItem.valueOf(result); } + /** + * Based on XPath 3.1 op:numeric-integer-divide. + * + * @param dividend + * the number to be divided + * @param divisor + * the number to divide by + * @return the quotient + */ @NonNull public static IIntegerItem opNumericIntegerDivide(@NonNull INumericItem dividend, @NonNull INumericItem divisor) { IIntegerItem retval; @@ -517,7 +924,7 @@ public static IIntegerItem opNumericIntegerDivide(@NonNull INumericItem dividend /** * Based on XPath 3.1 func:numeric-mod. + * "https://www.w3.org/TR/xpath-functions-31/#func-numeric-mod">op:numeric-mod. * * @param dividend * the number to be divided @@ -547,6 +954,14 @@ public static INumericItem opNumericMod(@NonNull INumericItem dividend, @NonNull return retval; } + /** + * Based on XPath 3.1 op:numeric-unary-minus. + * + * @param item + * the number whose sign is to be reversed + * @return the number with a reversed sign + */ @NonNull public static INumericItem opNumericUnaryMinus(@NonNull INumericItem item) { INumericItem retval; @@ -570,6 +985,17 @@ public static INumericItem opNumericUnaryMinus(@NonNull INumericItem item) { return retval; } + /** + * Based on XPath 3.1 op:numeric-equal. + * + * @param arg1 + * the first number to check for equality + * @param arg2 + * the second number to check for equality + * @return {@code true} if the numbers are numerically equal or {@code false} + * otherwise + */ @NonNull public static IBooleanItem opNumericEqual(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { IBooleanItem retval; @@ -583,6 +1009,17 @@ public static IBooleanItem opNumericEqual(@Nullable INumericItem arg1, @Nullable return retval; } + /** + * Based on XPath 3.1 op:numeric-greater-than. + * + * @param arg1 + * the first number to check + * @param arg2 + * the second number to check + * @return {@code true} if the first number is greater than or equal to the + * second, or {@code false} otherwise + */ @NonNull public static IBooleanItem opNumericGreaterThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { IBooleanItem retval; @@ -598,6 +1035,17 @@ public static IBooleanItem opNumericGreaterThan(@Nullable INumericItem arg1, @Nu return retval; } + /** + * Based on XPath 3.1 op:numeric-less-than. + * + * @param arg1 + * the first number to check + * @param arg2 + * the second number to check + * @return {@code true} if the first number is less than or equal to the second, + * or {@code false} otherwise + */ @NonNull public static IBooleanItem opNumericLessThan(@Nullable INumericItem arg1, @Nullable INumericItem arg2) { IBooleanItem retval; @@ -613,6 +1061,17 @@ public static IBooleanItem opNumericLessThan(@Nullable INumericItem arg1, @Nulla return retval; } + /** + * Based on XPath 3.1 op:boolean-equal. + * + * @param arg1 + * the first boolean to check + * @param arg2 + * the second boolean to check + * @return {@code true} if the first boolean is equal to the second, or + * {@code false} otherwise + */ @NonNull public static IBooleanItem opBooleanEqual(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { boolean left = arg1 != null && arg1.toBoolean(); @@ -621,6 +1080,17 @@ public static IBooleanItem opBooleanEqual(@Nullable IBooleanItem arg1, @Nullable return IBooleanItem.valueOf(left == right); } + /** + * Based on XPath 3.1 op:boolean-greater-than. + * + * @param arg1 + * the first boolean to check + * @param arg2 + * the second boolean to check + * @return {@code true} if the first argument is {@link IBooleanItem#TRUE} and + * the second is {@link IBooleanItem#FALSE}, or {@code false} otherwise + */ @NonNull public static IBooleanItem opBooleanGreaterThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { boolean left = arg1 != null && arg1.toBoolean(); @@ -629,6 +1099,17 @@ public static IBooleanItem opBooleanGreaterThan(@Nullable IBooleanItem arg1, @Nu return IBooleanItem.valueOf(left && !right); } + /** + * Based on XPath 3.1 op:boolean-less-than. + * + * @param arg1 + * the first boolean to check + * @param arg2 + * the second boolean to check + * @return {@code true} if the first argument is {@link IBooleanItem#FALSE} and + * the second is {@link IBooleanItem#TRUE}, or {@code false} otherwise + */ @NonNull public static IBooleanItem opBooleanLessThan(@Nullable IBooleanItem arg1, @Nullable IBooleanItem arg2) { boolean left = arg1 != null && arg1.toBoolean(); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/SequenceTypeImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/SequenceTypeImpl.java index 2db8d2ce8..d4dfca126 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/SequenceTypeImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/SequenceTypeImpl.java @@ -27,14 +27,18 @@ package gov.nist.secauto.metaschema.core.metapath.function; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.TypeSystem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.Objects; +import edu.umd.cs.findbugs.annotations.NonNull; + class SequenceTypeImpl implements ISequenceType { private final Class type; private final Occurrence occurrence; - public SequenceTypeImpl(Class type, Occurrence occurrence) { + public SequenceTypeImpl(@NonNull Class type, @NonNull Occurrence occurrence) { Objects.requireNonNull(type, "type"); Objects.requireNonNull(occurrence, "occurrence"); this.type = type; @@ -65,12 +69,15 @@ public String toString() { public String toSignature() { StringBuilder builder = new StringBuilder(); + Class type = getType(); // name - builder.append(getType().getName()) + builder.append(type == null + ? "" + : TypeSystem.getName(type)) // occurrence .append(getOccurrence().getIndicator()); - return builder.toString(); + return ObjectUtils.notNull(builder.toString()); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java index f30461b7e..d5b3d9a64 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/UriFunctionException.java @@ -98,7 +98,7 @@ public UriFunctionException(int code, Throwable cause) { } @Override - protected String getCodePrefix() { + public String getCodePrefix() { return "FONS"; } 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 new file mode 100644 index 000000000..c3ac3a69c --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayAppend.java @@ -0,0 +1,107 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayAppend { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("append") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("appendage") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArrayAppend::execute) + .build(); + + private ArrayAppend() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( + arguments.get(0).getFirstItem(true))); + @SuppressWarnings("unchecked") T appendage = (T) arguments.get(1).toCollectionValue(); + + return ISequence.of(append(array, appendage)); + } + + /** + * An implementation of XPath 3.1 array:append. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param appendage + * the Metapath item to append to the identified array + * @return a new array containing the modification + */ + @NonNull + public static IArrayItem append( + @NonNull IArrayItem array, + @NonNull T appendage) { + + List copy = new ArrayList<>(array); + copy.add(appendage); + + return IArrayItem.ofCollection(copy); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayFlatten.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayFlatten.java new file mode 100644 index 000000000..39f0f77c5 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayFlatten.java @@ -0,0 +1,110 @@ +/* + * 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.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayFlatten { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("flatten") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("input") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IItem.class) + .returnZeroOrMore() + .functionHandler(ArrayFlatten::execute) + .build(); + + private ArrayFlatten() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + ISequence input = ObjectUtils.requireNonNull(arguments.get(0)); + + return ISequence.of(flatten(input)); + } + + /** + * An implementation of XPath 3.1 array:flatten. + * + * @param items + * the items to flatten + * @return the stream of flattened items + */ + @SuppressWarnings("null") + @NonNull + public static Stream flatten(@NonNull List items) { + return items.stream() + .flatMap(ArrayFlatten::flatten); + } + + /** + * An implementation of XPath 3.1 array:flatten. + * + * @param item + * the item to flatten + * @return the stream of flattened items + */ + @SuppressWarnings("null") + @NonNull + public static Stream flatten(@NonNull IItem item) { + return item instanceof IArrayItem + // flatten the array members + ? ((IArrayItem) item).stream() + .flatMap(member -> member.asSequence().stream() + .flatMap(ArrayFlatten::flatten)) + // use the item + : ObjectUtils.notNull(Stream.of(item)); + } +} 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 new file mode 100644 index 000000000..b03ccaeb5 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayGet.java @@ -0,0 +1,135 @@ +/* + * 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.IIntegerItem; +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.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayGet { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("get") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("position") + .type(IIntegerItem.class) + .one() + .build()) + .returnType(IItem.class) + .returnZeroOrOne() + .functionHandler(ArrayGet::execute) + .build(); + + private ArrayGet() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + + return get(array, position).asSequence(); + } + + /** + * An implementation of XPath 3.1 array:get. + * + * @param + * the type of items in the given Metapath array + * @param target + * the array of Metapath items that is the target of retrieval + * @param positionItem + * 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 + */ + @NonNull + public static T get( + @NonNull List target, + @NonNull IIntegerItem positionItem) { + return get(target, positionItem.asInteger().intValue()); + } + + /** + * An implementation of XPath 3.1 array:get. + * + * @param + * the type of items in the given Metapath array + * @param target + * the array of Metapath items that is the target of retrieval + * @param position + * 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 + */ + @NonNull + public static T get( + @NonNull List target, + int position) { + try { + return ObjectUtils.requireNonNull(target.get(position - 1)); + } catch (IndexOutOfBoundsException ex) { + throw new ArrayException( + ArrayException.INDEX_OUT_OF_BOUNDS, + String.format("The index %d is outside the range of values for the array size '%d'.", + position, + target.size()), + ex); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayHead.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayHead.java new file mode 100644 index 000000000..ac5f1d285 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayHead.java @@ -0,0 +1,93 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public final class ArrayHead { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("head") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .returnType(IItem.class) + .returnZeroOrOne() + .functionHandler(ArrayHead::execute) + .build(); + + private ArrayHead() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + + ICollectionValue result = head(array); + return result == null ? ISequence.empty() : result.asSequence(); + } + + /** + * An implementation of XPath 3.1 array:head. + * + * @param + * the type of items in the given Metapath array + * @param array + * the array to get the head item from + * @return the head item + */ + @Nullable + public static T head(@NonNull IArrayItem array) { + return array.isEmpty() ? null : array.get(0); + } +} 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 new file mode 100644 index 000000000..94568f1e1 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayInsertBefore.java @@ -0,0 +1,142 @@ +/* + * 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.IIntegerItem; +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.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayInsertBefore { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("insert-before") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("position") + .type(IIntegerItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("member") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArrayInsertBefore::execute) + .build(); + + private ArrayInsertBefore() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + 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)).toCollectionValue(); + + return ISequence.of(insertBefore(array, position, member)); + } + + /** + * An implementation of XPath 3.1 array:insert-before. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param positionItem + * the integer position of the item to insert before + * @param member + * the Metapath item to insert into the identified array + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem insertBefore( + @NonNull IArrayItem array, + @NonNull IIntegerItem positionItem, + @NonNull T member) { + return insertBefore(array, positionItem.asInteger().intValueExact(), member); + } + + /** + * An implementation of XPath 3.1 array:insert-before. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param position + * the integer position of the item to insert before + * @param member + * the Metapath item to insert into the identified array + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem insertBefore( + @NonNull IArrayItem array, + int position, + @NonNull T member) { + return ArrayJoin.join(ObjectUtils.notNull(List.of( + ArraySubarray.subarray(array, 1, position - 1), + IArrayItem.of(member), + ArraySubarray.subarray(array, position)))); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayJoin.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayJoin.java new file mode 100644 index 000000000..d8d7aa7a3 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayJoin.java @@ -0,0 +1,96 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayJoin { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("join") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .zeroOrMore() + .build()) + .returnType(IItem.class) + .returnZeroOrOne() + .functionHandler(ArrayJoin::execute) + .build(); + + private ArrayJoin() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + ISequence> arrays = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); + + return join(arrays).asSequence(); + } + + /** + * An implementation of XPath 3.1 array:join. + * + * @param + * the type of items in the given Metapath array + * @param arrays + * the arrays to join + * @return a new combined array + */ + @NonNull + public static IArrayItem join( + @NonNull Collection> arrays) { + return IArrayItem.ofCollection(ObjectUtils.notNull(arrays.stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()))); + } +} 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 new file mode 100644 index 000000000..6777a14d2 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayPut.java @@ -0,0 +1,152 @@ +/* + * 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.IIntegerItem; +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.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayPut { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("put") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("position") + .type(IIntegerItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("member") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArrayPut::execute) + .build(); + + private ArrayPut() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + 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).toCollectionValue(); + + return put(array, position, member).asSequence(); + } + + /** + * An implementation of XPath 3.1 array:put. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param positionItem + * the integer position of the item to replace + * @param member + * the Metapath item to replace the identified array member with + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem put( + @NonNull IArrayItem array, + @NonNull IIntegerItem positionItem, + @NonNull T member) { + return put(array, positionItem.asInteger().intValueExact(), member); + } + + /** + * An implementation of XPath 3.1 array:put. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param position + * the integer position of the item to replace + * @param member + * the Metapath item to replace the identified array member with + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem put( + @NonNull IArrayItem array, + int position, + @NonNull T member) { + List copy = new ArrayList<>(array); + try { + copy.set(position - 1, member); + } catch (IndexOutOfBoundsException ex) { + throw new ArrayException( + ArrayException.INDEX_OUT_OF_BOUNDS, + String.format("The position %d is outside the range of values for the array of size '%d'.", + position, + copy.size()), + ex); + } + + return IArrayItem.ofCollection(copy); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java new file mode 100644 index 000000000..d3791f3a7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayRemove.java @@ -0,0 +1,142 @@ +/* + * 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.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.IIntegerItem; +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.util.ObjectUtils; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayRemove { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("remove") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("positions") + .type(IIntegerItem.class) + .zeroOrMore() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArrayRemove::execute) + .build(); + + private ArrayRemove() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( + arguments.get(0).getFirstItem(true))); + ISequence positions = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); + + return ISequence.of(removeItems(array, positions)); + } + + /** + * An implementation of XPath 3.1 array:remove. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param positions + * the integer position of the items to remove + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem removeItems( + @NonNull IArrayItem array, + @NonNull Collection positions) { + return remove( + array, + ObjectUtils.notNull(positions.stream() + .map(position -> position.asInteger().intValueExact()) + .collect(Collectors.toSet()))); + } + + /** + * An implementation of XPath 3.1 array:remove. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param positions + * the integer position of the items to remove + * @return a new array containing the modification + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @NonNull + public static IArrayItem remove( + @NonNull IArrayItem array, + @NonNull Collection positions) { + Set positionSet = positions instanceof Set ? (Set) positions : new HashSet<>(positions); + + List remaining = ObjectUtils.notNull(IntStream.range(1, array.size() + 1) + .filter(index -> !positionSet.contains(index)) + .mapToObj(index -> array.get(index - 1)) + .collect(Collectors.toList())); + + return IArrayItem.ofCollection(remaining); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayReverse.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayReverse.java new file mode 100644 index 000000000..fba9e9ca7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayReverse.java @@ -0,0 +1,97 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.metaschema.core.metapath.function.library; + +import gov.nist.secauto.metaschema.core.metapath.DynamicContext; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArrayReverse { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("reverse") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArrayReverse::execute) + .build(); + + private ArrayReverse() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( + arguments.get(0).getFirstItem(true))); + + return ISequence.of(reverse(array)); + } + + /** + * An implementation of XPath 3.1 array:reverse. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @return a new array containing the modification + */ + @NonNull + public static IArrayItem reverse( + @NonNull IArrayItem array) { + List copy = new ArrayList<>(array); + Collections.reverse(copy); + return IArrayItem.ofCollection(copy); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java new file mode 100644 index 000000000..26370a45c --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySize.java @@ -0,0 +1,80 @@ +/* + * 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.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.IIntegerItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class ArraySize { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("size") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .returnType(IIntegerItem.class) + .returnOne() + .functionHandler(ArraySize::execute) + .build(); + + /** + * An implementation of XPath 3.1 array:size. + * + * @param array + * the arrays to join + * @return a new combined array + */ + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + + return ISequence.of(IIntegerItem.valueOf(array.size())); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java new file mode 100644 index 000000000..90d830535 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArraySubarray.java @@ -0,0 +1,241 @@ +/* + * 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.IIntegerItem; +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.util.ObjectUtils; + +import java.util.ArrayList; +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class ArraySubarray { + @NonNull + public static final IFunction SIGNATURE_TWO_ARG = IFunction.builder() + .name("subarray") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("start") + .type(IIntegerItem.class) + .one() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArraySubarray::executeTwoArg) + .build(); + @NonNull + public static final IFunction SIGNATURE_THREE_ARG = IFunction.builder() + .name("subarray") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("start") + .type(IIntegerItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("length") + .type(IIntegerItem.class) + .one() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(ArraySubarray::executeThreeArg) + .build(); + + private ArraySubarray() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> executeTwoArg(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( + arguments.get(0).getFirstItem(true))); + IIntegerItem start = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + + return ISequence.of(subarray(array, start)); + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> executeThreeArg(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull( + arguments.get(0).getFirstItem(true))); + IIntegerItem start = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + IIntegerItem length = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(2).getFirstItem(true))); + + return ISequence.of(subarray(array, start, length)); + } + + /** + * An implementation of XPath 3.1 array:subarray. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param startItem + * the integer position of the item to start with (inclusive) + * @return a new array item consisting of the items in the identified range + * @throws ArrayException + * if the position is not in the range of 1 to array:size + */ + @SuppressWarnings("PMD.OnlyOneReturn") + @NonNull + public static IArrayItem subarray( + @NonNull IArrayItem array, + @NonNull IIntegerItem startItem) { + return subarray(array, startItem.asInteger().intValueExact()); + } + + /** + * An implementation of XPath 3.1 array:subarray. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param startItem + * the integer position of the item to start with (inclusive) + * @param lengthItem + * the integer count of items to include starting with the item at the + * start position + * @return a new array item consisting of the items in the identified range + * @throws ArrayException + * if the length is negative or the position is not in the range of 1 + * to array:size + */ + @SuppressWarnings("PMD.OnlyOneReturn") + @NonNull + public static IArrayItem subarray( + @NonNull IArrayItem array, + @NonNull IIntegerItem startItem, + @NonNull IIntegerItem lengthItem) { + return subarray(array, startItem.asInteger().intValueExact(), lengthItem.asInteger().intValueExact()); + } + + /** + * An implementation of XPath 3.1 array:subarray. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param start + * the integer position of the item to start with (inclusive) + * @return a new array item consisting of the items in the identified range + * @throws ArrayException + * if the length is negative or the position is not in the range of 1 + * to array:size + */ + @NonNull + public static IArrayItem subarray( + @NonNull IArrayItem array, + int start) { + return subarray(array, start, array.size() - start + 1); + } + + /** + * An implementation of XPath 3.1 array:subarray. + * + * @param + * the type of items in the given Metapath array + * @param array + * the target Metapath array + * @param start + * the integer position of the item to start with (inclusive) + * @param length + * the integer count of items to include starting with the item at the + * start position + * @return a new array item consisting of the items in the identified range + * @throws ArrayException + * if the length is negative or the position is not in the range of 1 + * to array:size + */ + @NonNull + public static IArrayItem subarray( + @NonNull IArrayItem array, + int start, + int length) { + if (length < 0) { + throw new ArrayException( + ArrayException.NEGATIVE_ARRAY_LENGTH, String.format("The length '%d' is negative.", length)); + } + + List copy; + try { + copy = array.subList(start - 1, start - 1 + length); + } catch (IndexOutOfBoundsException ex) { + throw new ArrayException( + ArrayException.INDEX_OUT_OF_BOUNDS, + String.format("The start + length (%d + %d) exceeds the array length '%d'.", + start, + length, + array.size()), + ex); + } + + return IArrayItem.ofCollection(new ArrayList<>(copy)); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayTail.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayTail.java new file mode 100644 index 000000000..d45ed09df --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/ArrayTail.java @@ -0,0 +1,91 @@ +/* + * 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.ISequence; +import gov.nist.secauto.metaschema.core.metapath.MetapathConstants; +import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; +import gov.nist.secauto.metaschema.core.metapath.function.IArgument; +import gov.nist.secauto.metaschema.core.metapath.function.IFunction; +import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public final class ArrayTail { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("tail") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("array") + .type(IArrayItem.class) + .one() + .build()) + .returnType(IItem.class) + .returnZeroOrOne() + .functionHandler(ArrayTail::execute) + .build(); + + private ArrayTail() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IArrayItem array = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + + return ISequence.of(tail(array)); + } + + /** + * An implementation of XPath 3.1 array:tail. + * + * @param + * the type of items in the given Metapath array + * @param array + * the array to get the tail items from + * @return the tail items + */ + @Nullable + public static IArrayItem tail(@NonNull IArrayItem array) { + return ArraySubarray.subarray(array, 2); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java index f6c45a1c8..7a08f52fb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/CastFunction.java @@ -75,6 +75,9 @@ static IFunction signature( return IFunction.builder() .name(name) .namespace(namespace) + .deterministic() + .contextIndependent() + .focusIndependent() .argument(IArgument.builder() .name("arg1") .type(IAnyAtomicItem.class) @@ -105,7 +108,7 @@ public ISequence execute(@NonNull IFunction function, ISequence arg = FunctionUtils.asType( ObjectUtils.notNull(arguments.get(0))); - IAnyAtomicItem item = FunctionUtils.getFirstItem(arg, true); + IAnyAtomicItem item = arg.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } 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 21fd664bc..2b40c53a2 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 @@ -132,6 +132,7 @@ public DefaultFunctionLibrary() { // NOPMD - intentional // https://www.w3.org/TR/xpath-functions-31/#func-month-from-date // https://www.w3.org/TR/xpath-functions-31/#func-month-from-dateTime // https://www.w3.org/TR/xpath-functions-31/#func-months-from-duration + // https://www.w3.org/TR/xpath-functions-31/#func-node-name // https://www.w3.org/TR/xpath-functions-31/#func-normalize-space // https://www.w3.org/TR/xpath-functions-31/#func-normalize-unicode // https://www.w3.org/TR/xpath-functions-31/#func-not @@ -164,6 +165,7 @@ public DefaultFunctionLibrary() { // NOPMD - intentional registerFunction(FnStartsWith.SIGNATURE); // https://www.w3.org/TR/xpath-functions-31/#func-static-base-uri registerFunction(FnStaticBaseUri.SIGNATURE); + // P0: https://www.w3.org/TR/xpath-functions-31/#func-string // P1: https://www.w3.org/TR/xpath-functions-31/#func-string-join // P1: https://www.w3.org/TR/xpath-functions-31/#func-string-length // P1: https://www.w3.org/TR/xpath-functions-31/#func-subsequence @@ -191,29 +193,91 @@ public DefaultFunctionLibrary() { // NOPMD - intentional // https://www.w3.org/TR/xpath-functions-31/#func-years-from-duration // P2: https://www.w3.org/TR/xpath-functions-31/#func-zero-or-one - // xpath casting functions - registerFunction( - CastFunction.signature(MetapathConstants.NS_XML_SCHEMA, "boolean", IBooleanItem.class, IBooleanItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "date", IDateItem.class, IDateItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "dateTime", IDateTimeItem.class, IDateTimeItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "decimal", IDecimalItem.class, IDecimalItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "duration", IDurationItem.class, IDurationItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "integer", IIntegerItem.class, IIntegerItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "NCName", INcNameItem.class, INcNameItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "nonNegativeInteger", INonNegativeIntegerItem.class, - INonNegativeIntegerItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "positiveInteger", IPositiveIntegerItem.class, - IPositiveIntegerItem::cast)); - registerFunction(CastFunction.signature( - MetapathConstants.NS_XML_SCHEMA, "string", IStringItem.class, IStringItem::cast)); + // https://www.w3.org/TR/xpath-functions-31/#func-array-get + registerFunction(ArrayGet.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-size + registerFunction(ArraySize.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-put + registerFunction(ArrayPut.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-append + registerFunction(ArrayAppend.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-subarray + registerFunction(ArraySubarray.SIGNATURE_TWO_ARG); + registerFunction(ArraySubarray.SIGNATURE_THREE_ARG); + // https://www.w3.org/TR/xpath-functions-31/#func-array-remove + registerFunction(ArrayRemove.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-insert-before + registerFunction(ArrayInsertBefore.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-head + registerFunction(ArrayHead.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-tail + registerFunction(ArrayTail.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-reverse + registerFunction(ArrayReverse.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-array-join + registerFunction(ArrayJoin.SIGNATURE); + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-for-each + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-filter + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-fold-left + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-fold-right + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-for-each-pair + // P3: https://www.w3.org/TR/xpath-functions-31/#func-array-sort + // https://www.w3.org/TR/xpath-functions-31/#func-array-flatten + registerFunction(ArrayFlatten.SIGNATURE); + + // https://www.w3.org/TR/xpath-functions-31/#func-map-merge + registerFunction(MapMerge.SIGNATURE_ONE_ARG); + registerFunction(MapMerge.SIGNATURE_TWO_ARG); + // https://www.w3.org/TR/xpath-functions-31/#func-map-size + registerFunction(MapSize.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-keys + registerFunction(MapKeys.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-contains + registerFunction(MapContains.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-get + registerFunction(MapGet.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-find + registerFunction(MapFind.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-put + registerFunction(MapPut.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-entry + registerFunction(MapEntry.SIGNATURE); + // https://www.w3.org/TR/xpath-functions-31/#func-map-remove + registerFunction(MapRemove.SIGNATURE); + // P3: https://www.w3.org/TR/xpath-functions-31/#func-map-for-each + + // // xpath casting functions + // registerFunction( + // CastFunction.signature(MetapathConstants.NS_XML_SCHEMA, "boolean", + // IBooleanItem.class, IBooleanItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "date", IDateItem.class, IDateItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "dateTime", IDateTimeItem.class, + // IDateTimeItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "decimal", IDecimalItem.class, + // IDecimalItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "duration", IDurationItem.class, + // IDurationItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "integer", IIntegerItem.class, + // IIntegerItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "NCName", INcNameItem.class, + // INcNameItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "nonNegativeInteger", + // INonNegativeIntegerItem.class, + // INonNegativeIntegerItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "positiveInteger", + // IPositiveIntegerItem.class, + // IPositiveIntegerItem::cast)); + // registerFunction(CastFunction.signature( + // MetapathConstants.NS_XML_SCHEMA, "string", IStringItem.class, + // IStringItem::cast)); // metapath casting functions registerFunction(CastFunction.signature( diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java index 0d0a999f8..98c99d588 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAbs.java @@ -76,10 +76,9 @@ private static ISequence execute( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - ISequence sequence = FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(0))); + ISequence sequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvg.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvg.java index 2f829a3c5..b339475ac 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvg.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnAvg.java @@ -93,7 +93,7 @@ private static ISequence execute( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - List items = sequence.asList(); + List items = sequence.getValue(); return ISequence.of(average(items)); } @@ -163,7 +163,7 @@ public static IAnyAtomicItem average(@NonNull Collection type.getName()) + .map(Class::getName) .collect(CustomCollectors.joiningWithOxfordComma(",")))); } @@ -172,6 +172,7 @@ public static IAnyAtomicItem average(@NonNull Collection R average( @NonNull Collection items, diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java index 91486eda6..e193bbe9c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBaseUri.java @@ -101,7 +101,7 @@ private static ISequence executeOneArg(@NonNull IFunction function, ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INodeItem item = FunctionUtils.getFirstItem(arg, true); + INodeItem item = arg.getFirstItem(true); return ISequence.of(fnBaseUri(item)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java index 4d044095a..bafdf43b4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnBoolean.java @@ -29,7 +29,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 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.function.InvalidArgumentFunctionException; @@ -109,7 +108,7 @@ public static IBooleanItem fnBoolean(@NonNull ISequence sequence) { */ public static boolean fnBooleanAsPrimitive(@NonNull ISequence sequence) { boolean retval = false; - IItem first = FunctionUtils.getFirstItem(sequence, false); + IItem first = sequence.getFirstItem(false); if (first != null) { if (first instanceof INodeItem) { retval = true; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java index 215ede52b..7d847da3e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeiling.java @@ -79,7 +79,7 @@ private static ISequence execute( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java index ad5fc26b6..f6fbf7516 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCompare.java @@ -35,7 +35,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.IItem; 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.util.ObjectUtils; import java.util.List; @@ -80,11 +79,8 @@ private static ISequence execute( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - IStringItem comparand1 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))), true); - - IStringItem comparand2 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true); + IStringItem comparand1 = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true)); + IStringItem comparand2 = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); ISequence retval; if (comparand1 == null || comparand2 == null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java index 56eaf357f..42594b5f9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnConcat.java @@ -29,7 +29,6 @@ import gov.nist.secauto.metaschema.core.metapath.DynamicContext; 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; @@ -87,7 +86,7 @@ private static ISequence execute( return ISequence.of(concat(ObjectUtils.notNull(arguments.stream() .map(arg -> { assert arg != null; - return (IAnyAtomicItem) FunctionUtils.getFirstItem(arg, true); + return (IAnyAtomicItem) arg.getFirstItem(true); })))); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java index f860e9bf2..54c2619ca 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnData.java @@ -36,6 +36,7 @@ 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.IAtomicValuedItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IArrayItem; import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -126,11 +127,9 @@ private static ISequence executeOneArg( */ @SuppressWarnings("null") @NonNull - public static ISequence fnData(@NonNull ISequence sequence) { - @NonNull Stream stream = sequence.asStream(); - return ISequence.of(stream.flatMap(x -> { - return Stream.of(fnDataItem(x)); - })); + public static ISequence fnData(@NonNull ISequence sequence) { + return ISequence.of(sequence.stream() + .flatMap(FnData::atomize)); } /** @@ -145,13 +144,60 @@ public static ISequence fnData(@NonNull ISequence sequence) { @NonNull public static IAnyAtomicItem fnDataItem(@NonNull IItem item) { IAnyAtomicItem retval = null; - if (item instanceof IAnyAtomicItem) { - retval = (IAnyAtomicItem) item; - } else if (item instanceof IAtomicValuedItem) { + if (item instanceof IAtomicValuedItem) { retval = ((IAtomicValuedItem) item).toAtomicItem(); } - if (retval == null) { + if (retval != null) { + return retval; + } + throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, item); + } + + /** + * An implementation of + * item + * atomization. + * + * @param item + * the item to atomize + * @return the atomized result + */ + @NonNull + public static Stream fnDataItem(@NonNull IArrayItem item) { + return ObjectUtils.notNull(item.stream().flatMap(member -> { + Stream result; + if (member instanceof IItem) { + result = atomize((IItem) member); + } else if (member instanceof ISequence) { + result = ((ISequence) member).stream() + .flatMap(FnData::atomize); + } else { + throw new UnsupportedOperationException("array member not an item or sequence."); + } + return result; + })); + } + + /** + * An implementation of + * item + * atomization. + * + * @param item + * the item to atomize + * @return the atomized result + */ + @NonNull + public static Stream atomize(@NonNull IItem item) { + Stream retval; + if (item instanceof IAnyAtomicItem) { + retval = ObjectUtils.notNull(Stream.of((IAnyAtomicItem) item)); + } else if (item instanceof IAtomicValuedItem) { + retval = ObjectUtils.notNull(Stream.of(((IAtomicValuedItem) item).toAtomicItem())); + } else if (item instanceof IArrayItem) { + retval = fnDataItem((IArrayItem) item); + } else { throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, item); } return retval; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java index efb74b3a0..049e4b58e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDoc.java @@ -76,7 +76,7 @@ private static ISequence execute(@NonNull IFunction function, IItem focus) { ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IStringItem item = FunctionUtils.getFirstItem(arg, true); + IStringItem item = arg.getFirstItem(true); return item == null ? ISequence.empty() : ISequence.of(fnDoc(item, dynamicContext)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java index 2e02b919e..64c8f602f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnDocumentUri.java @@ -88,14 +88,9 @@ private static ISequence executeNoArg(@NonNull IFunction function, INodeItem item = FunctionUtils.requireTypeOrNull(INodeItem.class, focus); - ISequence retval; - if (item instanceof IDocumentNodeItem) { - IAnyUriItem uri = fnDocumentUri((IDocumentNodeItem) item); - retval = ISequence.of(uri); - } else { - retval = ISequence.empty(); - } - return retval; + return item instanceof IDocumentNodeItem + ? ISequence.of(fnDocumentUri((IDocumentNodeItem) item)) + : ISequence.empty(); } @SuppressWarnings("unused") @@ -107,18 +102,11 @@ private static ISequence executeOneArg(@NonNull IFunction function, ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - INodeItem item = FunctionUtils.getFirstItem(arg, true); - - ISequence retval; - if (item == null) { - retval = ISequence.empty(); - } else if (item instanceof IDocumentNodeItem) { - IAnyUriItem uri = fnDocumentUri((IDocumentNodeItem) item); - retval = ISequence.of(uri); - } else { - retval = ISequence.empty(); - } - return retval; + INodeItem item = arg.getFirstItem(true); + + return item instanceof IDocumentNodeItem + ? ISequence.of(fnDocumentUri((IDocumentNodeItem) item)) + : ISequence.empty(); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java index 0189f99a0..879a37f27 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnInsertBefore.java @@ -84,11 +84,10 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence target = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IIntegerItem position - = ObjectUtils.requireNonNull( - FunctionUtils.getFirstItem(FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true)); + + IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); ISequence inserts = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(2))); - return ISequence.of(fnInsertBefore(target, position, inserts)); + return ISequence.ofCollection(fnInsertBefore(target, position, inserts)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMax.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMax.java index 0035a917e..8953bcad3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMax.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnMinMax.java @@ -101,7 +101,7 @@ private static ISequence executeMin( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - return ISequence.of(min(sequence.asList())); + return ISequence.of(min(sequence.getValue())); } @SuppressWarnings("unused") @@ -114,7 +114,7 @@ private static ISequence executeMax( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - return ISequence.of(max(sequence.asList())); + return ISequence.of(max(sequence.getValue())); } /** @@ -179,7 +179,7 @@ private static Stream normalize( int size = resultingItems.size(); if (counts.getOrDefault(IStringItem.class, 0) + counts.getOrDefault(IAnyUriItem.class, 0) == size) { stream = resultingItems.stream() - .map(item -> item.asStringItem()); + .map(IAnyAtomicItem::asStringItem); } else if (counts.getOrDefault(IDecimalItem.class, 0) == size) { stream = resultingItems.stream() .map(item -> (IDecimalItem) item); @@ -191,7 +191,7 @@ private static Stream normalize( InvalidArgumentFunctionException.INVALID_ARGUMENT_TYPE, String.format("Values must all be of a single atomic type. Their types are '%s'.", FunctionUtils.getTypes(resultingItems).stream() - .map(clazz -> clazz.getName()) + .map(Class::getName) .collect(Collectors.joining(",")))); } return stream; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java index 1242f9bbc..bd06bdd36 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnPath.java @@ -121,7 +121,7 @@ private static ISequence executeOneArg(@NonNull IFunction function, */ @NonNull public static ISequence fnPath(@NonNull ISequence sequence) { - IItem item = FunctionUtils.getFirstItem(sequence, true); + IItem item = sequence.getFirstItem(true); ISequence retval; if (item == null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java index 9568d11d3..73f666bf2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRemove.java @@ -79,10 +79,8 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence target = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IIntegerItem position - = ObjectUtils.requireNonNull( - FunctionUtils.getFirstItem(FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true)); - return ISequence.of(fnRemove(target, position)); + IIntegerItem position = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + return ISequence.ofCollection(fnRemove(target, position)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java index fbfe91ec3..210932977 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnResolveUri.java @@ -102,7 +102,7 @@ private static ISequence executeOneArg( return ISequence.empty(); // NOPMD - readability } - IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true); + IStringItem relativeString = relativeSequence.getFirstItem(true); IAnyUriItem resolvedUri = null; if (relativeString != null) { resolvedUri = fnResolveUri(relativeString, null, dynamicContext); @@ -143,7 +143,7 @@ private static ISequence executeTwoArg( } ISequence baseSequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); - IStringItem baseString = FunctionUtils.getFirstItem(baseSequence, true); + IStringItem baseString = baseSequence.getFirstItem(true); if (baseString == null) { throw new InvalidArgumentFunctionException( @@ -152,7 +152,7 @@ private static ISequence executeTwoArg( } IAnyUriItem baseUri = IAnyUriItem.cast(baseString); - IStringItem relativeString = FunctionUtils.getFirstItem(relativeSequence, true); + IStringItem relativeString = relativeSequence.getFirstItem(true); IAnyUriItem resolvedUri = null; if (relativeString != null) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnReverse.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnReverse.java index 5c6b39fa4..8fac330e3 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnReverse.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnReverse.java @@ -74,7 +74,7 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence target = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - return ISequence.of(fnReverse(target)); + return ISequence.ofCollection(fnReverse(target)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java index 5edef5cf5..7b39a3643 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnRound.java @@ -102,7 +102,7 @@ private static ISequence executeOneArg( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } @@ -120,15 +120,12 @@ private static ISequence executeTwoArg( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } - IIntegerItem precision = FunctionUtils.asType( - FunctionUtils.requireFirstItem( - ObjectUtils.requireNonNull(arguments.get(1)), - true)); + IIntegerItem precision = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); return ISequence.of(item.round(precision)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java index 33320a005..43faba83d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStartsWith.java @@ -35,7 +35,6 @@ import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.util.List; @@ -48,6 +47,9 @@ public final class FnStartsWith { static final IFunction SIGNATURE = IFunction.builder() .name("starts-with") .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) + .deterministic() + .contextDependent() + .focusIndependent() .argument(IArgument.builder() .name("arg1").type(IStringItem.class) .zeroOrOne() @@ -68,10 +70,9 @@ private static ISequence execute(@NonNull IFunction function, @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - IStringItem arg1 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))), true); - IStringItem arg2 = FunctionUtils.getFirstItem( - FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))), true); + IStringItem arg1 = FunctionUtils.asTypeOrNull(arguments.get(0).getFirstItem(true)); + + IStringItem arg2 = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); return ISequence.of(fnStartsWith(arg1, arg2)); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStaticBaseUri.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStaticBaseUri.java index 1fc6a1b73..9e5565fdf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStaticBaseUri.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnStaticBaseUri.java @@ -46,6 +46,9 @@ public final class FnStaticBaseUri { static final IFunction SIGNATURE = IFunction.builder() .name("static-base-uri") .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS) + .deterministic() + .contextDependent() + .focusIndependent() .argument(IArgument.builder() .name("arg1") .type(IStringItem.class) diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java index 93c418e83..2275c7ee4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnSum.java @@ -105,7 +105,7 @@ private static ISequence executeOneArg( ISequence sequence = FunctionUtils.asType( ObjectUtils.requireNonNull(arguments.get(0))); - return ISequence.of(sum(sequence.asList(), IIntegerItem.ZERO)); + return ISequence.of(sum(sequence.getValue(), IIntegerItem.ZERO)); } @SuppressWarnings("unused") @@ -115,15 +115,11 @@ private static ISequence executeTwoArg( @NonNull List> arguments, @NonNull DynamicContext dynamicContext, IItem focus) { - ISequence sequence = FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(0))); + ISequence sequence = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IAnyAtomicItem zero = FunctionUtils.getFirstItem( - FunctionUtils.asType( - ObjectUtils.requireNonNull(arguments.get(1))), - true); + IAnyAtomicItem zero = FunctionUtils.asTypeOrNull(arguments.get(1).getFirstItem(true)); - return ISequence.of(sum(sequence.asList(), zero)); + return ISequence.of(sum(sequence.getValue(), zero)); } /** @@ -208,7 +204,7 @@ public static IAnyAtomicItem sum( InvalidArgumentFunctionException.INVALID_ARGUMENT_TYPE, String.format("Values must all be of type '%s'.", OperationFunctions.AGGREGATE_MATH_TYPES.stream() - .map(type -> type.getName()) + .map(Class::getName) .collect(CustomCollectors.joiningWithOxfordComma(",")))); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTail.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTail.java index 1f07291d6..0ecb66551 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTail.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnTail.java @@ -73,7 +73,7 @@ private static ISequence execute(@NonNull IFunction function, @NonNull DynamicContext dynamicContext, IItem focus) { ISequence items = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - return ISequence.of(fnTail(items)); + return ISequence.ofCollection(fnTail(items)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapContains.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapContains.java new file mode 100644 index 000000000..b9639d970 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapContains.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.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.atomic.IBooleanItem; +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; + +public final class MapContains { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("contains") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("key") + .type(IAnyAtomicItem.class) + .one() + .build()) + .returnType(IBooleanItem.class) + .returnOne() + .functionHandler(MapContains::execute) + .build(); + + private MapContains() { + // disable construction + } + + @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))); + + return IBooleanItem.valueOf(contains(map, key)).asSequence(); + } + + /** + * An implementation of XPath 3.1 map:contains. + * + * @param + * the type of items in the given Metapath map + * @param map + * the map of Metapath items that is the target of retrieval + * @param key + * the key for the item to retrieve + * @return {@code true} if the key exists in the map, or {@code false} otherwise + */ + public static boolean contains( + @NonNull IMapItem map, + @NonNull IAnyAtomicItem key) { + return map.get(key.asMapKey()) != null; + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapEntry.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapEntry.java new file mode 100644 index 000000000..c1e018a3e --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapEntry.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.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.IMapItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MapEntry { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("entry") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("key") + .type(IAnyAtomicItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("item") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IItem.class) + .returnOne() + .functionHandler(MapEntry::execute) + .build(); + + private MapEntry() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence> execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + IAnyAtomicItem key = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0).getFirstItem(true))); + @SuppressWarnings("unchecked") T value = (T) arguments.get(1).toCollectionValue(); + + return entry(key, value).asSequence(); + } + + /** + * An implementation of XPath 3.1 map:entry. + * + * @param + * the type of items in the given Metapath map + * @param key + * the Metapath map entry key + * @param value + * the Metapath map entry value + * @return a new array containing the modification + */ + @NonNull + public static IMapItem entry( + @NonNull IAnyAtomicItem key, + @NonNull T value) { + return IMapItem.ofEntries(IMapItem.entry(key, value)); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapFind.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapFind.java new file mode 100644 index 000000000..b5a4b39c7 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapFind.java @@ -0,0 +1,180 @@ +/* + * 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.IArrayItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MapFind { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("find") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("input") + .type(IItem.class) + .zeroOrMore() + .build()) + .argument(IArgument.builder() + .name("key") + .type(IAnyAtomicItem.class) + .one() + .build()) + .returnType(IArrayItem.class) + .returnOne() + .functionHandler(MapFind::execute) + .build(); + + private MapFind() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence execute(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + ISequence input = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); + IAnyAtomicItem key = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + + return ISequence.of(IArrayItem.ofCollection( + ObjectUtils.notNull(find((Collection) input, key) + .collect(Collectors.toList())))); + } + + /** + * An implementation of XPath 3.1 map:find. + * + * @param items + * the item sequence to search for key matches + * @param key + * the key for the item to retrieve + * @return the retrieved item + */ + @NonNull + public static Stream find( + @NonNull Collection items, + @NonNull IAnyAtomicItem key) { + return ObjectUtils.notNull(items.stream() + // handle item + .flatMap(item -> find(ObjectUtils.notNull(item), key))); + } + + /** + * An implementation of XPath 3.1 map:find. + * + * @param item + * the item to search for key matches + * @param key + * the key for the item to retrieve + * @return the retrieved item + */ + @NonNull + public static Stream find( + @NonNull IItem item, + @NonNull IAnyAtomicItem key) { + Stream retval; + if (item instanceof IArrayItem) { + IArrayItem array = (IArrayItem) item; + retval = ObjectUtils.notNull(array.stream() + // handle array values + .flatMap(value -> find(ObjectUtils.notNull(value), key))); + } else if (item instanceof IMapItem) { + IMapItem map = (IMapItem) item; + // handle map + retval = find(map, key); + } else { + // do nothing + retval = ObjectUtils.notNull(Stream.empty()); + } + return retval; + } + + @NonNull + private static Stream find( + @NonNull ICollectionValue value, + @NonNull IAnyAtomicItem key) { + Stream retval; + if (value instanceof ISequence) { + ISequence sequence = (ISequence) value; + // handle sequence items + retval = find((Collection) sequence, key); + } else { + // handle item + retval = find((IItem) value, key); + } + return retval; + } + + /** + * An implementation of XPath 3.1 map:find. + *

+ * This is a specialized method for processing an item that is a map item, which + * can be searched for a key in a much more efficient way. + * + * @param item + * the item to search for key matches + * @param key + * the key for the item to retrieve + * @return the retrieved item + */ + @NonNull + public static Stream find( + @NonNull IMapItem item, + @NonNull IAnyAtomicItem key) { + return ObjectUtils.notNull(Stream.concat( + // add matching value, if it exists + Stream.ofNullable(MapGet.get(item, key)), + item.values().stream() + // handle map values + .flatMap(value -> find(ObjectUtils.notNull(value), key)))); + } +} 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..60c69716d --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapGet.java @@ -0,0 +1,104 @@ +/* + * 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.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 final class MapGet { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("get") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .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(); + + private MapGet() { + // disable construction + } + + @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 map:get. + * + * @param + * the type of items in the given Metapath map + * @param map + * the map of Metapath items that is the target of retrieval + * @param key + * the key for the item to retrieve + * @return the retrieved item + */ + @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/function/library/MapKeys.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapKeys.java new file mode 100644 index 000000000..c64b55794 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapKeys.java @@ -0,0 +1,94 @@ +/* + * 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.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.IMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MapKeys { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("keys") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .returnType(IAnyAtomicItem.class) + .returnZeroOrMore() + .functionHandler(MapKeys::execute) + .build(); + + private MapKeys() { + // disable construction + } + + @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))); + + return ISequence.of(keys(map)); + } + + /** + * An implementation of XPath 3.1 map:keys. + * + * @param map + * the map to get the keys for + * @return a stream of map keys + */ + @SuppressWarnings("null") + @NonNull + public static Stream keys(@NonNull Map map) { + return map.keySet().stream() + .map(IMapKey::getKey); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapMerge.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapMerge.java new file mode 100644 index 000000000..8b6a9dd76 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapMerge.java @@ -0,0 +1,228 @@ +/* + * 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.function.JsonFunctionException; +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.IStringItem; +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.CollectionUtil; +import gov.nist.secauto.metaschema.core.util.CustomCollectors; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public final class MapMerge { + private static final Random RANDOM = new Random(); + private static final IMapKey DUPLICATES_OPTION = IStringItem.valueOf("duplicates").asMapKey(); + + @NonNull + static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() + .name("merge") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("maps") + .type(IMapItem.class) + .zeroOrMore() + .build()) + .returnType(IMapItem.class) + .returnOne() + .functionHandler(MapMerge::executeOneArg) + .build(); + + @NonNull + static final IFunction SIGNATURE_TWO_ARG = IFunction.builder() + .name("merge") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("maps") + .type(IMapItem.class) + .zeroOrMore() + .build()) + .argument(IArgument.builder() + .name("options") + .type(IMapItem.class) + .one() + .build()) + .returnType(IMapItem.class) + .returnOne() + .functionHandler(MapMerge::executeTwoArg) + .build(); + + private enum Duplicates { + REJECT("reject", (key, v1, v2) -> { + throw new JsonFunctionException( + JsonFunctionException.DUPLICATE_KEYS, + String.format("Duplicate key '%s' not allowed.", key.getKey().asString())); + }), + + USE_FIRST("use-first", (key, v1, v2) -> v1), + USE_LAST("use-last", (key, v1, v2) -> v2), + USE_ANY("use-any", (key, v1, v2) -> RANDOM.nextBoolean() ? v1 : v2), + @SuppressWarnings("null") + COMBINE("combine", (key, v1, v2) -> { + return Stream.concat( + v1.asSequence().stream(), + v2.asSequence().stream()).collect(ISequence.toSequence()); + }); + + private static final Map BY_NAME; + + @NonNull + private final String name; + @NonNull + private final CustomCollectors.DuplicateHandler duplicateHander; + + static { + @SuppressWarnings("PMD.UseConcurrentHashMap") Map map = new HashMap<>(); + for (Duplicates value : values()) { + map.put(value.getName(), value); + } + BY_NAME = Collections.unmodifiableMap(map); + } + + @Nullable + public static Duplicates lookup(@NonNull String name) { + return BY_NAME.get(name); + } + + Duplicates(@NonNull String name, + @NonNull CustomCollectors.DuplicateHandler duplicateHander) { + this.name = name; + this.duplicateHander = duplicateHander; + } + + public String getName() { + return name; + } + + public CustomCollectors.DuplicateHandler getDuplicateHander() { + return duplicateHander; + } + } + + private MapMerge() { + // disable construction + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence executeOneArg(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + ISequence> maps = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); + + return ISequence.of(merge(maps, CollectionUtil.emptyMap())); + } + + @SuppressWarnings("unused") + @NonNull + private static ISequence executeTwoArg(@NonNull IFunction function, + @NonNull List> arguments, + @NonNull DynamicContext dynamicContext, + IItem focus) { + ISequence> maps = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); + IMapItem options = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1).getFirstItem(true))); + + return ISequence.of(merge(maps, options)); + } + + /** + * An implementation of XPath 3.1 map:merge. + * + * @param maps + * a collection of maps to merge + * @param options + * settings that affect the merge behavior + * @return a map containing the merged entries + */ + @SuppressWarnings({ "null", "PMD.OnlyOneReturn" }) + @NonNull + public static IMapItem merge(@NonNull Collection> maps, + @NonNull Map options) { + if (maps.isEmpty()) { + return IMapItem.empty(); + } + + // handle the "duplicates" option + ICollectionValue duplicatesOption = options.get(DUPLICATES_OPTION); + + Duplicates duplicates; + if (duplicatesOption == null) { + // default option + duplicates = Duplicates.USE_FIRST; + } else { + // resolve the provided option + IAnyAtomicItem atomicValue = FnData.fnData(duplicatesOption.asSequence()).getFirstItem(true); + if (atomicValue == null) { + throw new JsonFunctionException( + JsonFunctionException.INVALID_OPTION, + String.format("Missing '%s' option value.", DUPLICATES_OPTION.getKey().asString())); + } + String duplicatesValue = IStringItem.cast(atomicValue).asString(); + duplicates = Duplicates.lookup(duplicatesValue); + if (duplicates == null) { + throw new JsonFunctionException( + JsonFunctionException.INVALID_OPTION, + String.format("Invalid '%s' option value '%s'.", DUPLICATES_OPTION.getKey().asString(), duplicatesValue)); + } + } + + // merge the maps + return IMapItem.ofCollection(maps.stream() + .flatMap(map -> map.entrySet().stream()) + // collect the entries into a new map + .collect(CustomCollectors.toMap(Map.Entry::getKey, Map.Entry::getValue, duplicates.getDuplicateHander(), + HashMap::new))); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapPut.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapPut.java new file mode 100644 index 000000000..a621d6cec --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapPut.java @@ -0,0 +1,117 @@ +/* + * 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.IMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MapPut { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("put") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("key") + .type(IAnyAtomicItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("value") + .type(IItem.class) + .zeroOrMore() + .build()) + .returnType(IMapItem.class) + .returnOne() + .functionHandler(MapPut::execute) + .build(); + + private MapPut() { + // disable construction + } + + @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))); + @SuppressWarnings("unchecked") V value = (V) ObjectUtils.requireNonNull(arguments.get(2)).toCollectionValue(); + + return put(map, key, value).asSequence(); + } + + /** + * An implementation of XPath 3.1 map:put. + * + * @param + * the type of items in the given Metapath map + * @param map + * the map of Metapath items that is to be modified + * @param key + * the key for the value to add to the map + * @param value + * the value to add to the map + * @return the modified map + */ + @NonNull + public static IMapItem put( + @NonNull IMapItem map, + @NonNull IAnyAtomicItem key, + @NonNull V value) { + @SuppressWarnings("PMD.UseConcurrentHashMap") Map copy = new HashMap<>(map); + copy.put(key.asMapKey(), value); + + return IMapItem.ofCollection(copy); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapRemove.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapRemove.java new file mode 100644 index 000000000..313c939b4 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapRemove.java @@ -0,0 +1,115 @@ +/* + * 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.IMapItem; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public final class MapRemove { + @NonNull + public static final IFunction SIGNATURE = IFunction.builder() + .name("remove") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .argument(IArgument.builder() + .name("keys") + .type(IAnyAtomicItem.class) + .zeroOrMore() + .build()) + .returnType(IMapItem.class) + .returnOne() + .functionHandler(MapRemove::execute) + .build(); + + private MapRemove() { + // disable construction + } + + @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))); + ISequence keys = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); + + return ISequence.of(removeItems(map, keys)); + } + + /** + * An implementation of XPath 3.1 map:remove. + * + * @param + * the type of items in the given Metapath map + * @param map + * the map of Metapath items that is to be modified + * @param keys + * the keys to remove from the map + * @return the modified map + */ + @NonNull + public static IMapItem removeItems( + @NonNull IMapItem map, + @NonNull Collection keys) { + Set keySet = keys.stream() + .map(IAnyAtomicItem::asMapKey) + .collect(Collectors.toSet()); + + Map remaining = ObjectUtils.notNull(map.entrySet().stream() + .filter(entry -> !keySet.contains(entry.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); + + return IMapItem.ofCollection(remaining); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapSize.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapSize.java new file mode 100644 index 000000000..6f9c6ffa3 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MapSize.java @@ -0,0 +1,80 @@ +/* + * 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.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.IIntegerItem; +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; + +public class MapSize { + @NonNull + static final IFunction SIGNATURE = IFunction.builder() + .name("size") + .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) + .deterministic() + .contextIndependent() + .focusIndependent() + .argument(IArgument.builder() + .name("map") + .type(IMapItem.class) + .one() + .build()) + .returnType(IIntegerItem.class) + .returnOne() + .functionHandler(MapSize::execute) + .build(); + + /** + * An implementation of XPath 3.1 map:size. + * + * @param array + * the arrays to join + * @return a new combined array + */ + @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))); + + return ISequence.of(IIntegerItem.valueOf(map.size())); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java index 5e3e83139..d8bfdd423 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/MpRecurseDepth.java @@ -105,7 +105,7 @@ private static ISequence executeOneArg( ISequence initalContext = ISequence.of(FunctionUtils.requireType(INodeItem.class, focus)); ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); - IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true); + IStringItem recursionPath = ObjectUtils.requireNonNull(arg.getFirstItem(true)); return recurseDepth(initalContext, recursionPath, dynamicContext); } @@ -121,7 +121,7 @@ private static ISequence executeTwoArg( ISequence initalContext = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0))); ISequence arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1))); - IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true); + IStringItem recursionPath = ObjectUtils.requireNonNull(arg.getFirstItem(true)); return recurseDepth(initalContext, recursionPath, dynamicContext); } @@ -162,12 +162,12 @@ public static ISequence recurseDepth( @NonNull MetapathExpression recursionMetapath, @NonNull DynamicContext dynamicContext) { - return ISequence.of(ObjectUtils.notNull(initialContext.asStream() + return ISequence.of(ObjectUtils.notNull(initialContext.stream() .flatMap(item -> { @NonNull ISequence metapathResult = recursionMetapath.evaluate(item, dynamicContext); ISequence result = recurseDepth(metapathResult, recursionMetapath, dynamicContext); - return ObjectUtils.notNull(Stream.concat(result.asStream(), Stream.of(item))); + return ObjectUtils.notNull(Stream.concat(result.stream(), Stream.of(item))); }))); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java index 09244ee9f..076bb9064 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/library/NumericFunction.java @@ -63,6 +63,9 @@ static IFunction signature(@NonNull String namespace, @NonNull String name, @Non return IFunction.builder() .name(name) .namespace(namespace) + .deterministic() + .contextIndependent() + .focusIndependent() .argument(IArgument.builder() .name("arg1") .type(INumericItem.class) @@ -103,7 +106,7 @@ public ISequence execute(@NonNull IFunction function, return ISequence.empty(); // NOPMD - readability } - INumericItem item = FunctionUtils.getFirstItem(sequence, true); + INumericItem item = sequence.getFirstItem(true); if (item == null) { return ISequence.empty(); // NOPMD - readability } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java new file mode 100644 index 000000000..ee02e2a45 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractArrayItem.java @@ -0,0 +1,112 @@ +/* + * 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.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.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 AbstractArrayItem + extends ImmutableCollections.AbstractImmutableDelegatedList + implements IArrayItem { + @NonNull + public static final QName QNAME = new QName("array"); + @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("position").type(IIntegerItem.class).one().build())); + @NonNull + public static final ISequenceType RESULT = ISequenceType.of(IAnyAtomicItem.class, Occurrence.ZERO_OR_ONE); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @NonNull + private static final IArrayItem EMPTY = new ArrayItemN<>(); + + /** + * Get an immutable array item that is empty. + * + * @param + * the item Java type + * @return the empty array item + */ + @SuppressWarnings("unchecked") + @NonNull + public static IArrayItem empty() { + return (IArrayItem) EMPTY; + } + + @Override + public ISequence execute(List> arguments, DynamicContext dynamicContext, + ISequence focus) { + ISequence arg = FunctionUtils.asType( + ObjectUtils.notNull(arguments.get(0))); + + IIntegerItem position = arg.getFirstItem(true); + if (position == null) { + return ISequence.empty(); // NOPMD - readability + } + + int index = position.asInteger().intValueExact() - 1; + ICollectionValue result = getValue().get(index); + return result.asSequence(); + } + + @Override + public int hashCode() { + return Objects.hash(getValue()); + } + + @Override + public boolean equals(Object other) { + return other == this + || other instanceof IArrayItem && getValue().equals(((IArrayItem) other).getValue()); + } + + @Override + public String asString() { + return ObjectUtils.notNull(toString()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java new file mode 100644 index 000000000..69cf6052f --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractKeySpecifier.java @@ -0,0 +1,300 @@ +/* + * 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.InvalidTypeMetapathException; +import gov.nist.secauto.metaschema.core.metapath.cst.IExpression; +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.IKeySpecifier; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A common base class for all key specifiers. + */ +public abstract class AbstractKeySpecifier implements IKeySpecifier { + @Override + public Stream lookup( + @NonNull IItem targetItem, + @NonNull DynamicContext dynamicContext, + @NonNull ISequence focus) { + Stream result; + if (targetItem instanceof IArrayItem) { + result = lookupInArray((IArrayItem) targetItem, dynamicContext, focus); + } else if (targetItem instanceof IMapItem) { + result = lookupInMap((IMapItem) targetItem, dynamicContext, focus); + } else { + throw new InvalidTypeMetapathException(targetItem, + String.format("Item type '%s' is not an array or map.", targetItem.getClass().getName())); + } + return result; + } + + /** + * A dispatch method intended to handle lookups within an array item. + * + * @param targetItem + * the item to query + * @param dynamicContext + * the dynamic context to use for expression evaluation + * @param focus + * the focus item for expression evaluation + * @return a stream of collection values matching this key specifier + */ + @NonNull + protected abstract Stream lookupInArray( + @NonNull IArrayItem targetItem, + @NonNull DynamicContext dynamicContext, + @NonNull ISequence focus); + + /** + * A dispatch method intended to handle lookups within a map item. + * + * @param targetItem + * the item to query + * @param dynamicContext + * the dynamic context to use for expression evaluation + * @param focus + * the focus item for expression evaluation + * @return a stream of collection values matching this key specifier + */ + @NonNull + protected abstract Stream lookupInMap( + @NonNull IMapItem targetItem, + @NonNull DynamicContext dynamicContext, + @NonNull ISequence focus); + + /** + * Construct a new key specifier supporting name-based lookups. + * + * @param name + * the name to use for lookups + * @return the key specifier + */ + @NonNull + public static IKeySpecifier newNameKeySpecifier(@NonNull String name) { + return new AbstractKeySpecifier.NcNameKeySpecifier(name); + } + + /** + * Construct a new key specifier supporting integer-based lookups. + * + * @param integer + * the integer to use for lookups + * @return the key specifier + */ + @NonNull + public static IKeySpecifier newIntegerLiteralKeySpecifier(@NonNull IIntegerItem integer) { + return new AbstractKeySpecifier.IntegerLiteralKeySpecifier(integer); + } + + /** + * Construct a new key specifier supporting wildcard lookups. + * + * @return the key specifier + */ + @NonNull + public static IKeySpecifier newWildcardKeySpecifier() { + return new AbstractKeySpecifier.WildcardKeySpecifier(); + } + + /** + * Construct a new key specifier supporting key-based lookups. + * + * @param keyExpression + * the expression used to get a key to use for lookups + * @return the key specifier + */ + @NonNull + public static IKeySpecifier newParenthesizedExprKeySpecifier(@NonNull IExpression keyExpression) { + return new AbstractKeySpecifier.ParenthesizedExprKeySpecifier(keyExpression); + } + + private static class NcNameKeySpecifier + extends AbstractKeySpecifier { + @NonNull + private final String name; + + public NcNameKeySpecifier(@NonNull String name) { + this.name = name; + } + + @NonNull + public String getName() { + return name; + } + + @Override + protected Stream lookupInArray( + IArrayItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + throw new InvalidTypeMetapathException(targetItem, + String.format("A name-based lookup '%s' is not appropriate for an array.", getName())); + } + + @Override + protected Stream lookupInMap( + IMapItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(Stream.ofNullable(MapGet.get(targetItem, IStringItem.valueOf(name)))); + } + } + + private static class IntegerLiteralKeySpecifier + extends AbstractKeySpecifier { + private final int index; + + public IntegerLiteralKeySpecifier(@NonNull IIntegerItem literal) { + index = literal.asInteger().intValueExact(); + } + + @Override + protected Stream lookupInArray( + IArrayItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + try { + return ObjectUtils.notNull(Stream.ofNullable(ArrayGet.get(targetItem, index))); + } catch (IndexOutOfBoundsException ex) { + throw new ArrayException( + ArrayException.INDEX_OUT_OF_BOUNDS, + String.format("The index '%d' is outside the range of values for the array size '%d'.", + index + 1, + targetItem.size()), + ex); + } + } + + @Override + protected Stream lookupInMap( + IMapItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(Stream.ofNullable(MapGet.get(targetItem, IIntegerItem.valueOf(index)))); + } + } + + private static class WildcardKeySpecifier + extends AbstractKeySpecifier { + + public WildcardKeySpecifier() { + // do nothing + } + + @Override + protected Stream lookupInArray( + IArrayItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(targetItem.stream()); + } + + @Override + protected Stream lookupInMap( + IMapItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + return ObjectUtils.notNull(targetItem.values().stream()); + } + } + + private static class ParenthesizedExprKeySpecifier + extends AbstractKeySpecifier { + @NonNull + private final IExpression keyExpression; + + public ParenthesizedExprKeySpecifier(@NonNull IExpression keyExpression) { + this.keyExpression = keyExpression; + } + + public IExpression getKeyExpression() { + return keyExpression; + } + + @Override + protected Stream lookupInArray( + IArrayItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + ISequence keys = FnData.fnData(getKeyExpression().accept(dynamicContext, focus)); + + return ObjectUtils.notNull(keys.stream() + .flatMap(key -> { + if (key instanceof IIntegerItem) { + int index = ((IIntegerItem) key).asInteger().intValueExact(); + try { + return Stream.ofNullable(ArrayGet.get(targetItem, index)); + } catch (IndexOutOfBoundsException ex) { + throw new ArrayException( + ArrayException.INDEX_OUT_OF_BOUNDS, + String.format("The index %d is outside the range of values for the array size '%d'.", + index + 1, + targetItem.size()), + ex); + } + } + throw new InvalidTypeMetapathException(targetItem, + String.format("The key '%s' of type '%s' is not appropriate for an array lookup.", + key.asString(), + key.getClass().getName())); + + })); + } + + @Override + protected Stream lookupInMap( + IMapItem targetItem, + DynamicContext dynamicContext, + ISequence focus) { + ISequence keys + = ObjectUtils.requireNonNull(FnData.fnData(getKeyExpression().accept(dynamicContext, focus))); + + return ObjectUtils.notNull(keys.stream() + .flatMap(key -> { + assert key != null; + return Stream.ofNullable(MapGet.get(targetItem, key)); + })); + } + } +} 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..5177e8704 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractMapItem.java @@ -0,0 +1,113 @@ +/* + * 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<>(); + + /** + * Get an immutable map item that is empty. + * + * @param + * the item Java type + * @return the empty map item + */ + + @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/SingletonSequenceImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java similarity index 66% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/SingletonSequenceImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java index 2f5244aba..e091098d9 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/SingletonSequenceImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractSequence.java @@ -24,76 +24,57 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath; +package gov.nist.secauto.metaschema.core.metapath.impl; +import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Stream; +import java.util.stream.Collectors; import edu.umd.cs.findbugs.annotations.NonNull; -class SingletonSequenceImpl implements ISequence { - @NonNull - private final ITEM_TYPE item; - - public SingletonSequenceImpl(@NonNull ITEM_TYPE item) { - this.item = item; - } +public abstract class AbstractSequence + extends ImmutableCollections.AbstractImmutableDelegatedList + implements ISequence { @NonNull - protected ITEM_TYPE getItem() { - return item; + private static final ISequence EMPTY = new SequenceN<>(); + + /** + * Get an immutable sequence that is empty. + * + * @param + * the item Java type + * @return the empty sequence + */ + @SuppressWarnings("unchecked") + public static ISequence empty() { + return (ISequence) EMPTY; } - @SuppressWarnings("null") @Override - public List asList() { - return List.of(item); + public boolean equals(Object other) { + // must either be the same instance or a sequence that has the same list + // contents + return other == this + || other instanceof ISequence && getValue().equals(((ISequence) other).getValue()); } - @SuppressWarnings("null") @Override - public Stream asStream() { - return Stream.of(item); + public int hashCode() { + return getValue().hashCode(); } @Override - public boolean isEmpty() { - return false; + public String asString() { + return ObjectUtils.notNull(toString()); } @Override public String toString() { - return asList().toString(); - } - - @Override - public int size() { - return 1; - } - - @Override - public ISequence collect() { - return this; - } - - @Override - public void forEach(Consumer action) { - action.accept(item); - } - - @Override - public boolean equals(Object other) { - // must either be the same instance or a sequence that has the same list - // contents - return other == this - || other instanceof ISequence && asList().equals(((ISequence) other).asList()); - } - - @Override - public int hashCode() { - return asList().hashCode(); + return safeStream() + .map(Object::toString) + .collect(Collectors.joining(",", "(", ")")); } } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IInstanceModelChoiceBinding.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java similarity index 78% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IInstanceModelChoiceBinding.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java index 7eeb41687..83bb7b822 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/metaschema/IInstanceModelChoiceBinding.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/AbstractStringMapKey.java @@ -24,16 +24,22 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.metaschema; +package gov.nist.secauto.metaschema.core.metapath.impl; -import gov.nist.secauto.metaschema.core.model.IChoiceInstance; +import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; -public interface IInstanceModelChoiceBinding - extends IBindingInstanceModelAbsolute, IChoiceInstance, IBindingContainerModelAbsolute { +public abstract class AbstractStringMapKey + implements IMapKey { @Override - IBindingDefinitionAssembly getOwningDefinition(); + public int hashCode() { + return getKey().asStringItem().hashCode(); + } @Override - IBindingDefinitionAssembly getContainingDefinition(); + public boolean equals(Object obj) { + return this == obj || + (obj instanceof AbstractStringMapKey + && getKey().asStringItem().equals(((AbstractStringMapKey) obj).getKey().asStringItem())); + } } diff --git a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/IFeatureBoundContainerFlag.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java similarity index 64% rename from databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/IFeatureBoundContainerFlag.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java index e5d2f7976..15142b6a4 100644 --- a/databind/src/main/java/gov/nist/secauto/metaschema/databind/model/impl/IFeatureBoundContainerFlag.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ArrayItemN.java @@ -24,40 +24,51 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.databind.model.impl; +package gov.nist.secauto.metaschema.core.metapath.impl; -import gov.nist.secauto.metaschema.core.model.IContainerFlagSupport; -import gov.nist.secauto.metaschema.core.model.IFeatureContainerFlag; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; -import gov.nist.secauto.metaschema.databind.model.IBoundContainerFlag; -import gov.nist.secauto.metaschema.databind.model.IBoundInstanceFlag; -import java.util.Collection; +import java.util.List; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; /** - * Provides default implementations based on a supporting glag container - * implementation. + * An array item that supports an unbounded number of items. + * + * @param + * the Java type of the items */ -public interface IFeatureBoundContainerFlag - extends IBoundContainerFlag, - IFeatureContainerFlag { - - @Override +public class ArrayItemN + extends AbstractArrayItem { @NonNull - IContainerFlagSupport getFlagContainer(); + private final List items; - @Override - @Nullable - default IBoundInstanceFlag getFlagInstanceByName(String name) { - return getFlagContainer().getFlagInstanceMap().get(name); + /** + * Construct a new array item with the provided items. + * + * @param items + * the items to add to the array + */ + @SafeVarargs + public ArrayItemN(@NonNull ITEM... items) { + this(ObjectUtils.notNull(List.of(items))); + } + + /** + * Construct a new array item using the items from the provided list. + * + * @param items + * a list containing the items to add to the array + */ + public ArrayItemN(@NonNull List items) { + this.items = CollectionUtil.unmodifiableList(items); } @Override - @NonNull - default Collection getFlagInstances() { - return ObjectUtils.notNull(getFlagContainer().getFlagInstanceMap().values()); + public List getValue() { + return items; } + } 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 new file mode 100644 index 000000000..8d9cc5e52 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/ImmutableCollections.java @@ -0,0 +1,306 @@ +/* + * 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.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; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * This implementation is inspired by the similar implementation provided by the + * JDK. + */ +@SuppressWarnings("PMD.MissingStaticMethodInNonInstantiatableClass") +public final class ImmutableCollections { + + private ImmutableCollections() { + // disable construction + } + + private static UnsupportedOperationException unsupported() { + return new UnsupportedOperationException("method not supported"); + } + + /** + * A base class for an immutable collection. + * + * @param + * the item Java type + */ + public abstract static class AbstractImmutableCollection + extends AbstractCollection { + + @Override + public final boolean add(T item) { + throw unsupported(); + } + + @Override + public final boolean addAll(Collection collection) { + throw unsupported(); + } + + @Override + public final void clear() { + throw unsupported(); + } + + @Override + public final boolean remove(Object obj) { + throw unsupported(); + } + + @Override + public final boolean removeAll(Collection collection) { + throw unsupported(); + } + + @Override + public final boolean removeIf(Predicate filter) { + throw unsupported(); + } + + @Override + public final boolean retainAll(Collection collection) { + throw unsupported(); + } + } + + /** + * A base class for an immutable list. + * + * @param + * the item Java type + */ + public abstract static class AbstractImmutableList + extends AbstractImmutableCollection + implements List { + + @Override + public boolean addAll(int index, Collection collection) { + throw unsupported(); + } + + @Override + public T set(int index, T element) { + throw unsupported(); + } + + @Override + public void add(int index, T element) { + throw unsupported(); + } + + @Override + public T remove(int index) { + throw unsupported(); + } + } + + /** + * A base class for an immutable list that wraps a list. + * + * @param + * the item Java type + */ + public abstract static class AbstractImmutableDelegatedList + extends AbstractImmutableList { + + /** + * Get the wrapped list. + * + * @return the list + */ + @NonNull + protected abstract List getValue(); + + @Override + public T get(int index) { + return getValue().get(index); + } + + @Override + public int indexOf(Object obj) { + return getValue().indexOf(obj); + } + + @Override + public Iterator iterator() { + return getValue().iterator(); + } + + @Override + public int lastIndexOf(Object obj) { + return getValue().lastIndexOf(obj); + } + + @Override + public ListIterator listIterator() { + return getValue().listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return getValue().listIterator(index); + } + + @Override + public int size() { + return getValue().size(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return getValue().subList(fromIndex, toIndex); + } + + @Override + public Stream stream() { + return ObjectUtils.notNull(getValue().stream()); + } + + @Override + public String toString() { + return getValue().toString(); + } + } + + /** + * A base class for an immutable map. + * + * @param + * the map key Java type + * @param + * the map value Java type + */ + 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 map) { + 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 function) { + throw unsupported(); + } + } + + /** + * A base class for an immutable map that wraps a map. + * + * @param + * the map key Java type + * @param + * the map value Java type + */ + public abstract static class AbstractImmutableDelegatedMap + extends AbstractImmutableMap { + + /** + * Get the wrapped map. + * + * @return the map + */ + @NonNull + protected abstract Map getValue(); + + @Override + public Set> entrySet() { + return Collections.unmodifiableSet(getValue().entrySet()); + } + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertVisitor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java similarity index 60% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertVisitor.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java index 6e725d6d5..19879d5a7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertVisitor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/MapItemN.java @@ -24,52 +24,51 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.metapath.impl; -import com.vladsch.flexmark.util.ast.Node; -import com.vladsch.flexmark.util.ast.NodeVisitorBase; +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 gov.nist.secauto.metaschema.core.datatype.markup.IMarkupString; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension.InsertAnchorNode; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Predicate; +import java.util.Map; import edu.umd.cs.findbugs.annotations.NonNull; -public class InsertVisitor - extends NodeVisitorBase { - @NonNull - private final List inserts - = new LinkedList<>(); +/** + * An map item that supports an unbounded number of entries. + * + * @param + * the Java type of the entry values + */ +public class MapItemN + extends AbstractMapItem { @NonNull - private final Predicate filter; + private final Map entries; - public InsertVisitor( - @NonNull Predicate filter) { - this.filter = filter; + /** + * Construct a new map item with the provided entries. + * + * @param entries + * the entries to add to the map + */ + @SafeVarargs + public MapItemN(@NonNull Map.Entry... entries) { + this(ObjectUtils.notNull(Map.ofEntries(entries))); } - public InsertVisitor processNode(@NonNull IMarkupString markup) { - visit(markup.getDocument()); - return this; + /** + * Construct a new map item using the entries from the provided map. + * + * @param entries + * a map containing the entries to add to the map + */ + public MapItemN(@NonNull Map entries) { + this.entries = CollectionUtil.unmodifiableMap(entries); } @Override - protected void visit(Node node) { - if (node instanceof InsertAnchorNode) { - InsertAnchorNode insert = (InsertAnchorNode) node; - if (filter.test(insert)) { - inserts.add((InsertAnchorNode) node); - } - } else { - visitChildren(node); - } - } - - @NonNull - public List getInserts() { - return inserts; + public Map getValue() { + return entries; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ListSequenceImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SequenceN.java similarity index 61% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ListSequenceImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SequenceN.java index 3bf294a4e..cf557cfcb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ListSequenceImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SequenceN.java @@ -24,7 +24,7 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath; +package gov.nist.secauto.metaschema.core.metapath.impl; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.util.CollectionUtil; @@ -33,68 +33,66 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; -class ListSequenceImpl implements ISequence { +/** + * A Metapath sequence supporting an unbounded number of items. + * + * @param + * the Java type of the items + */ +public class SequenceN + extends AbstractSequence { @NonNull - private final List items; - - public ListSequenceImpl(@NonNull Collection items) { - this(new ArrayList<>(items), false); - } - - public ListSequenceImpl(@NonNull List items, boolean copy) { + private final List items; + + /** + * Construct a new sequence with the provided items. + * + * @param items + * a collection containing the items to add to the sequence + * @param copy + * if {@code true} make a defensive copy of the list or {@code false} + * otherwise + */ + public SequenceN(@NonNull List items, boolean copy) { this.items = CollectionUtil.unmodifiableList(copy ? new ArrayList<>(items) : items); } - @Override - public List asList() { - return items; - } - - @Override - public Stream asStream() { - return ObjectUtils.notNull(items.stream()); - } - - @Override - public boolean isEmpty() { - return items.isEmpty(); + /** + * Construct a new sequence with the provided items. + * + * @param items + * the items to add to the sequence + */ + @SafeVarargs + public SequenceN(@NonNull ITEM... items) { + this(ObjectUtils.notNull(List.of(items)), false); } - @Override - public String toString() { - return asList().toString(); - } - - @Override - public int size() { - return items.size(); - } - - @Override - public ISequence collect() { - return this; - } - - @Override - public void forEach(Consumer action) { - items.forEach(action); + /** + * Construct a new sequence with the provided items. + * + * @param items + * a collection containing the items to add to the sequence + */ + public SequenceN(@NonNull Collection items) { + this(new ArrayList<>(items), false); } - @Override - public boolean equals(Object other) { - // must either be the same instance or a sequence that has the same list - // contents - return other == this - || other instanceof ISequence && asList().equals(((ISequence) other).asList()); + /** + * Construct a new sequence with the provided items. + * + * @param items + * a list containing the items to add to the sequence + */ + public SequenceN(@NonNull List items) { + this(items, false); } @Override - public int hashCode() { - return asList().hashCode(); + public List getValue() { + return items; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EmptyListImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SingletonSequence.java similarity index 70% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EmptyListImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SingletonSequence.java index 56e2bfbe4..8d0e046f4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EmptyListImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/SingletonSequence.java @@ -24,58 +24,61 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath; +package gov.nist.secauto.metaschema.core.metapath.impl; import gov.nist.secauto.metaschema.core.metapath.item.IItem; +import gov.nist.secauto.metaschema.core.util.CollectionUtil; -import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; -class EmptyListImpl implements ISequence { +import edu.umd.cs.findbugs.annotations.NonNull; - @SuppressWarnings("null") - @Override - public List asList() { - return Collections.emptyList(); +/** + * A Metapath sequence supporting a singleton item. + * + * @param + * the Java type of the items + */ +public class SingletonSequence + extends AbstractSequence { + @NonNull + private final ITEM item; + + /** + * Construct a new sequence with the provided item. + * + * @param item + * the item to add to the sequence + */ + public SingletonSequence(@NonNull ITEM item) { + this.item = item; } - @SuppressWarnings("null") @Override - public Stream asStream() { - return Stream.empty(); + public List getValue() { + return CollectionUtil.singletonList(item); } @Override public boolean isEmpty() { - return true; + return false; } @Override public int size() { - return 0; - } - - @Override - public ISequence collect() { - return this; - } - - @Override - public void forEach(Consumer action) { - // do nothing + return 1; } + @SuppressWarnings("null") @Override - public boolean equals(Object other) { - // must either be the same instance or a sequence that is empty - return other == this - || other instanceof ISequence && ((ISequence) other).isEmpty(); + public Stream stream() { + return Stream.of(item); } @Override - public int hashCode() { - return 1; + public void forEach(Consumer action) { + action.accept(item); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StreamSequenceImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/StreamSequence.java similarity index 68% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StreamSequenceImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/StreamSequence.java index 184cf4acb..3612b4a90 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/StreamSequenceImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/impl/StreamSequence.java @@ -24,7 +24,7 @@ * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. */ -package gov.nist.secauto.metaschema.core.metapath; +package gov.nist.secauto.metaschema.core.metapath.impl; import gov.nist.secauto.metaschema.core.metapath.item.IItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -37,25 +37,35 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class StreamSequenceImpl implements ISequence { - private Stream stream; - private List list; +/** + * A Metapath sequence supporting an unbounded number of items backed initially + * by a stream. + * + * @param + * the Java type of the items + */ +public class StreamSequence + extends AbstractSequence { + + private Stream stream; + private List list; - public StreamSequenceImpl(@NonNull Stream stream) { + /** + * Construct a new sequence using the provided item stream. + * + * @param stream + * the items to add to the sequence + */ + public StreamSequence(@NonNull Stream stream) { Objects.requireNonNull(stream, "stream"); this.stream = stream; } @Override - public boolean isEmpty() { - return asList().isEmpty(); - } - - @Override - public List asList() { + public List getValue() { synchronized (this) { if (list == null) { - list = asStream().collect(Collectors.toUnmodifiableList()); + list = stream().collect(Collectors.toUnmodifiableList()); } assert list != null; return list; @@ -63,8 +73,9 @@ public List asList() { } @Override - public Stream asStream() { - @NonNull Stream retval; + public Stream stream() { + @NonNull Stream retval; + // Ensure thread safety and prevent multiple consumptions of the stream synchronized (this) { if (list == null) { if (stream == null) { @@ -81,36 +92,7 @@ public Stream asStream() { } @Override - public void forEach(Consumer action) { - asStream().forEachOrdered(action); - } - - @Override - public String toString() { - return asList().toString(); - } - - @Override - public int size() { - return asList().size(); - } - - @Override - public ISequence collect() { - asList(); - return this; - } - - @Override - public boolean equals(Object other) { - // must either be the same instance or a sequence that has the same list - // contents - return other == this - || other instanceof ISequence && asList().equals(((ISequence) other).asList()); - } - - @Override - public int hashCode() { - return asList().hashCode(); + public void forEach(Consumer action) { + stream().forEachOrdered(action); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java index b1ea60637..92c4f4df8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/IItem.java @@ -27,8 +27,13 @@ package gov.nist.secauto.metaschema.core.metapath.item; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; +import gov.nist.secauto.metaschema.core.metapath.ICollectionValue; +import gov.nist.secauto.metaschema.core.metapath.ISequence; + +import java.util.stream.Stream; + +public interface IItem extends ICollectionValue { -public interface IItem { /** * Get the item's "wrapped" value. This "wrapped" value may be: *