diff --git a/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/ProcessorVersion.java b/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/ProcessorVersion.java new file mode 100644 index 000000000..b9450bf16 --- /dev/null +++ b/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/ProcessorVersion.java @@ -0,0 +1,57 @@ + + +package gov.nist.secauto.metaschema.cli.processor; + +import gov.nist.secauto.metaschema.core.util.IVersionInfo; + +/** + * Provides version information for this library. + *

+ * This class exposes build-time metadata including version numbers, build + * timestamps, and Git repository information. + */ +public class ProcessorVersion implements IVersionInfo { + + private static final String NAME = "${project.name}"; + private static final String VERSION = "${project.version}"; + private static final String BUILD_TIMESTAMP = "${timestamp}"; + private static final String COMMIT = "@git.commit.id.abbrev@"; + private static final String BRANCH = "@git.branch@"; + private static final String CLOSEST_TAG = "@git.closest.tag.name@"; + private static final String ORIGIN = "@git.remote.origin.url@"; + + @Override + public String getName() { + return NAME; + } + + @Override + public String getVersion() { + return VERSION; + } + + @Override + public String getBuildTimestamp() { + return BUILD_TIMESTAMP; + } + + @Override + public String getGitOriginUrl() { + return ORIGIN; + } + + @Override + public String getGitCommit() { + return COMMIT; + } + + @Override + public String getGitBranch() { + return BRANCH; + } + + @Override + public String getGitClosestTag() { + return CLOSEST_TAG; + } +} diff --git a/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/Version.java b/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/Version.java deleted file mode 100644 index 5b87b3732..000000000 --- a/cli-processor/src/main/java-templates/gov/nist/secauto/metaschema/cli/processor/Version.java +++ /dev/null @@ -1,40 +0,0 @@ - - -package gov.nist.secauto.metaschema.cli.processor; - -import static org.fusesource.jansi.Ansi.ansi; - -import java.io.PrintStream; - -public class Version implements VersionInfo { - - public static final String VERSION = "${project.version}"; - public static final String BUILD_TIMESTAMP = "${timestamp}"; - public static final String COMMIT = "@git.commit.id.abbrev@"; - - public Version() { - } - - @Override - public String getVersion() { - return VERSION; - } - - @Override - public String getBuildTime() { - return BUILD_TIMESTAMP; - } - - @Override - public String getCommit() { - return COMMIT; - } - - @Override - public void generateExtraInfo(PrintStream out) { - out.println(ansi() - .a("Metaschema version ").bold().a(getVersion()).boldOff() - .a(" on commit ").bold().a(getCommit()).boldOff() - .a(" built at ").bold().a( getBuildTime()).reset()); - } -} 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 72ec3c8e0..d8e1667fa 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 @@ -13,6 +13,14 @@ import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * Records information about the exit status of a CLI command. + *

+ * This abstract class provides base functionality for handling CLI command exit + * statuses, including error logging and throwable management. Implementing + * classes must provide the {@link #getMessage()} implementation to define the + * status message content. + */ public abstract class AbstractExitStatus implements ExitStatus { private static final Logger LOGGER = LogManager.getLogger(AbstractExitStatus.class); @@ -61,8 +69,16 @@ public ExitStatus withThrowable(@NonNull Throwable throwable) { @Nullable protected abstract String getMessage(); - @Override - public void generateMessage(boolean showStackTrace) { + /** + * Determines the appropriate LogBuilder based on the exit code status. For + * non-positive exit codes (success/info), returns an INFO level builder. For + * positive exit codes (errors), returns an ERROR level builder. + * + * @return the appropriate LogBuilder based on exit status, or {@code null} if + * logging is disabled at the determined level + */ + @Nullable + private LogBuilder getLogBuilder() { LogBuilder logBuilder = null; if (getExitCode().getStatusCode() <= 0) { if (LOGGER.isInfoEnabled()) { @@ -71,25 +87,41 @@ public void generateMessage(boolean showStackTrace) { } else if (LOGGER.isErrorEnabled()) { logBuilder = LOGGER.atError(); } + return logBuilder; + } - if (logBuilder != null) { - if (showStackTrace && throwable != null) { - Throwable throwable = getThrowable(); - logBuilder.withThrowable(throwable); - } + /** + * Generates and logs a message based on the current exit status. The message is + * logged at either INFO level (for success/info status) or ERROR level (for + * error status). + * + * @param showStackTrace + * if {@code true} and a throwable is present, includes the stack trace + * in the log + */ + @Override + public void generateMessage(boolean showStackTrace) { + LogBuilder logBuilder = getLogBuilder(); + if (logBuilder == null) { + return; + } - String message = getMessage(); - if (message == null && throwable != null) { - message = throwable.getLocalizedMessage(); - } + boolean useStackTrace = showStackTrace && throwable != null; + if (useStackTrace) { + logBuilder.withThrowable(throwable); + } - if (message != null && !message.isEmpty()) { - logBuilder.log(message); - } else if (showStackTrace && throwable != null) { - // log the throwable - logBuilder.log(); - } + String message = getMessage(); + if (throwable != null && message == null) { + message = throwable.getLocalizedMessage(); } + + if (message != null && !message.isEmpty()) { + logBuilder.log(message); + } else if (useStackTrace) { + // log the throwable + logBuilder.log(); + } // else avoid an empty log line } } 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 f03af2c72..31a13a2a5 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 @@ -49,48 +49,69 @@ import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * Processes command line arguments and dispatches called commands. + *

+ * This implementation make significant use of the command pattern to support a + * delegation chain of commands based on implementations of {@link ICommand}. + */ +@SuppressWarnings("PMD.CouplingBetweenObjects") public class CLIProcessor { private static final Logger LOGGER = LogManager.getLogger(CLIProcessor.class); - @SuppressWarnings("null") + /** + * This option indicates if the help should be shown. + */ @NonNull - public static final Option HELP_OPTION = Option.builder("h") + public static final Option HELP_OPTION = ObjectUtils.notNull(Option.builder("h") .longOpt("help") .desc("display this help message") - .build(); - @SuppressWarnings("null") + .build()); + /** + * This option indicates if colorized output should be disabled. + */ @NonNull - public static final Option NO_COLOR_OPTION = Option.builder() + public static final Option NO_COLOR_OPTION = ObjectUtils.notNull(Option.builder() .longOpt("no-color") .desc("do not colorize output") - .build(); - @SuppressWarnings("null") + .build()); + /** + * This option indicates if non-errors should be suppressed. + */ @NonNull - public static final Option QUIET_OPTION = Option.builder("q") + public static final Option QUIET_OPTION = ObjectUtils.notNull(Option.builder("q") .longOpt("quiet") .desc("minimize output to include only errors") - .build(); - @SuppressWarnings("null") + .build()); + /** + * This option indicates if a strack trace should be shown for an error + * {@link ExitStatus}. + */ @NonNull - public static final Option SHOW_STACK_TRACE_OPTION = Option.builder() + public static final Option SHOW_STACK_TRACE_OPTION = ObjectUtils.notNull(Option.builder() .longOpt("show-stack-trace") .desc("display the stack trace associated with an error") - .build(); - @SuppressWarnings("null") + .build()); + /** + * This option indicates if the version information should be shown. + */ @NonNull - public static final Option VERSION_OPTION = Option.builder() + public static final Option VERSION_OPTION = ObjectUtils.notNull(Option.builder() .longOpt("version") .desc("display the application version") - .build(); - @SuppressWarnings("null") + .build()); + @NonNull - public static final List

+ * This uses the build-in version information. + * + * @param args + * the command line arguments + */ + public CLIProcessor(@NonNull String args) { + this(args, CollectionUtil.singletonMap(COMMAND_VERSION, new ProcessorVersion())); } + /** + * The main entry point for CLI processing. + *

+ * This uses the provided version information. + * + * @param exec + * the command name + * @param versionInfos + * the version info to display when the version option is provided + */ public CLIProcessor(@NonNull String exec, @NonNull Map versionInfos) { this.exec = exec; this.versionInfos = versionInfos; @@ -142,6 +186,12 @@ public Map getVersionInfos() { return versionInfos; } + /** + * Register a new command handler. + * + * @param handler + * the command handler to register + */ public void addCommandHandler(@NonNull ICommand handler) { commands.add(handler); } @@ -186,11 +236,21 @@ private ExitStatus parseCommand(String... args) { return status; } + /** + * Get the root-level commands. + * + * @return the list of commands + */ @NonNull protected final List getTopLevelCommands() { return CollectionUtil.unmodifiableList(commands); } + /** + * Get the root-level commands, mapped from name to command. + * + * @return the map of command names to command + */ @NonNull protected final Map getTopLevelCommandsByName() { return ObjectUtils.notNull(getTopLevelCommands() @@ -203,6 +263,9 @@ private static void handleNoColor() { AnsiConsole.systemUninstall(); } + /** + * Configure the logger to only report errors. + */ public static void handleQuiet() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); // NOPMD not closable here Configuration config = ctx.getConfiguration(); @@ -214,6 +277,9 @@ public static void handleQuiet() { } } + /** + * Output version information. + */ protected void showVersion() { @SuppressWarnings("resource") PrintStream out = AnsiConsole.out(); // NOPMD - not owner @@ -235,14 +301,12 @@ protected void showVersion() { out.flush(); } - // @SuppressWarnings("null") - // @NonNull - // public String[] getArgArray() { - // return Stream.concat(options.stream(), extraArgs.stream()).toArray(size -> - // new String[size]); - // } - - public class CallingContext { + /** + * Records information about the command line options and called command + * hierarchy. + */ + @SuppressWarnings("PMD.GodClass") + public final class CallingContext { @NonNull private final List

+ * The message arguments are stored in an unmodifiable list to ensure + * thread-safety and immutability. + */ public class MessageExitStatus extends AbstractExitStatus { private final List messageArguments; @@ -26,11 +34,8 @@ public class MessageExitStatus */ public MessageExitStatus(@NonNull ExitCode code, @NonNull Object... messageArguments) { super(code); - if (messageArguments.length == 0) { - this.messageArguments = Collections.emptyList(); - } else { - this.messageArguments = Arrays.asList(messageArguments); - } + this.messageArguments = CollectionUtil.unmodifiableList( + ObjectUtils.notNull(Arrays.asList(messageArguments))); } @Override diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/NonMessageExitStatus.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/NonMessageExitStatus.java index 8c0673e96..8cdfa64a9 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/NonMessageExitStatus.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/NonMessageExitStatus.java @@ -7,19 +7,33 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An {@link ExitStatus} implementation that represents a status without an + * associated message. + *

+ * This implementation is useful when only the exit code needs to be + * communicated, without additional context or explanation. + */ public class NonMessageExitStatus extends AbstractExitStatus { /** - * Construct a new message status. + * Construct a new exit status without an associated message. + * + * @param code + * the non-null exit code representing the status */ NonMessageExitStatus(@NonNull ExitCode code) { super(code); } + /** + * {@inheritDoc} + * + * @return {@code null} as this implementation does not support messages + */ @Override protected String getMessage() { - // always null return null; } } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/OptionUtils.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/OptionUtils.java index c918fee3b..b64822830 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/OptionUtils.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/OptionUtils.java @@ -9,12 +9,22 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A collection of utilities for handling command line options. + */ public final class OptionUtils { private OptionUtils() { // disable construction } + /** + * Generate the argument text for the given option. + * + * @param option + * the CLI option + * @return the argument text + */ @NonNull public static String toArgument(@NonNull Option option) { return option.hasLongOpt() ? "--" + option.getLongOpt() : "-" + option.getOpt(); diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/VersionInfo.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/VersionInfo.java deleted file mode 100644 index 5bd8b6863..000000000 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/VersionInfo.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.cli.processor; - -import java.io.PrintStream; - -import edu.umd.cs.findbugs.annotations.NonNull; - -public interface VersionInfo { - @NonNull - String getVersion(); - - @NonNull - String getBuildTime(); - - @NonNull - String getCommit(); - - void generateExtraInfo(@NonNull PrintStream out); -} diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractCommandExecutor.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractCommandExecutor.java index 16176edc9..046a1efa5 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractCommandExecutor.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractCommandExecutor.java @@ -12,24 +12,48 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A base class for implementation that perform the operation supported by a + * command. + */ public abstract class AbstractCommandExecutor implements ICommandExecutor { @NonNull private final CallingContext callingContext; @NonNull private final CommandLine commandLine; - public AbstractCommandExecutor( + /** + * Construct a new command executor. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + */ + protected AbstractCommandExecutor( @NonNull CallingContext callingContext, @NonNull CommandLine commandLine) { this.callingContext = callingContext; this.commandLine = commandLine; } + /** + * Get the context of the command execution, which provides access to the + * execution environment needed for command processing. + * + * @return the context + */ @NonNull protected CallingContext getCallingContext() { return callingContext; } + /** + * Get the parsed command line details containing the command options and + * arguments provided by the user during execution. + * + * @return the cli details + */ @NonNull protected CommandLine getCommandLine() { return commandLine; @@ -38,6 +62,11 @@ protected CommandLine getCommandLine() { @Override public abstract void execute() throws CommandExecutionException; + /** + * Get the command associated with this execution. + * + * @return the command + */ @NonNull protected ICommand getCommand() { return ObjectUtils.requireNonNull(getCallingContext().getTargetCommand()); 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 9655aa988..047f14e66 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 @@ -8,6 +8,7 @@ import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext; import gov.nist.secauto.metaschema.cli.processor.ExitCode; import gov.nist.secauto.metaschema.cli.processor.ExitStatus; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.apache.commons.cli.CommandLine; @@ -19,17 +20,30 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A base class for a command that supports hierarchical command structure with + * child commands. This class provides the foundation for implementing complex + * CLI commands that can have multiple levels of sub-commands. + *

+ * This class is thread-safe and supports concurrent access to command handlers. + */ public abstract class AbstractParentCommand implements ICommand { @NonNull private final Map commandToSubcommandHandlerMap; - private final boolean subCommandRequired; - @SuppressWarnings("null") - protected AbstractParentCommand(boolean subCommandRequired) { - this.commandToSubcommandHandlerMap = Collections.synchronizedMap(new LinkedHashMap<>()); - this.subCommandRequired = subCommandRequired; + /** + * Construct a new parent command. + */ + protected AbstractParentCommand() { + this.commandToSubcommandHandlerMap = ObjectUtils.notNull(Collections.synchronizedMap(new LinkedHashMap<>())); } + /** + * Add a child command. + * + * @param handler + * the command handler for the child command + */ protected final void addCommandHandler(ICommand handler) { String commandName = handler.getName(); this.commandToSubcommandHandlerMap.put(commandName, handler); @@ -40,15 +54,14 @@ public ICommand getSubCommandByName(String name) { return commandToSubcommandHandlerMap.get(name); } - @SuppressWarnings("null") @Override public Collection getSubCommands() { - return Collections.unmodifiableCollection(commandToSubcommandHandlerMap.values()); + return ObjectUtils.notNull(Collections.unmodifiableCollection(commandToSubcommandHandlerMap.values())); } @Override public boolean isSubCommandRequired() { - return subCommandRequired; + return true; } @Override @@ -57,9 +70,9 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c } @NonNull - protected ExitStatus executeCommand( + private ExitStatus executeCommand( @NonNull CallingContext callingContext, - @NonNull CommandLine commandLine) { + @SuppressWarnings("unused") @NonNull CommandLine commandLine) { callingContext.showHelp(); ExitStatus status; if (isSubCommandRequired()) { diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java index 65da54530..2d7668319 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/AbstractTerminalCommand.java @@ -12,41 +12,81 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; -import java.util.Collections; import edu.umd.cs.findbugs.annotations.NonNull; import nl.talsmasoftware.lazy4j.Lazy; +/** + * A base class for terminal commands in the command processing hierarchy. + * Terminal commands represent leaf nodes that perform actual operations and + * cannot have child commands. + */ public abstract class AbstractTerminalCommand implements ICommand { - private static Lazy currentWorkingDirectory = Lazy.lazy(() -> Paths.get("").toAbsolutePath()); - - @SuppressWarnings("null") - @Override - public Collection getSubCommands() { - return Collections.emptyList(); - } - - @Override - public boolean isSubCommandRequired() { - return false; - } + private static Lazy currentWorkingDirectory = Lazy.lazy(() -> Paths.get(System.getProperty("user.dir"))); + /** + * A utility method that can be used to get the current working directory. + *

+ * This method is thread-safe due to lazy initialization. + * + * @return the current working directory + */ @NonNull protected static Path getCurrentWorkingDirectory() { return ObjectUtils.notNull(currentWorkingDirectory.get()); } + /** + * A utility method that can be used to resolve a path against the current + * working directory. + *

+ * If the path is already absolute, then the provided path is returned. + * + * @param path + * the path to resolve + * + * @return the resolved path + */ @NonNull protected static Path resolveAgainstCWD(@NonNull Path path) { - return ObjectUtils.notNull(getCurrentWorkingDirectory().resolve(path).normalize()); + + return path.isAbsolute() + ? path + : ObjectUtils.notNull(getCurrentWorkingDirectory().resolve(path).normalize()); } + /** + * A utility method that can be used to resolve a URI against the URI for the + * current working directory. + *

+ * If the URI is already absolute, then the provided URI is returned. + *

+ * The path is normalized after resolution to remove any redundant name elements + * (like "." or ".."). + * + * + * @param uri + * the uri to resolve + * + * @return the resolved URI + */ @NonNull protected static URI resolveAgainstCWD(@NonNull URI uri) { - return ObjectUtils.notNull(getCurrentWorkingDirectory().toUri().resolve(uri.normalize())); + return uri.isAbsolute() + ? uri + : ObjectUtils.notNull(getCurrentWorkingDirectory().toUri().resolve(uri.normalize())); } + /** + * A utility method that can be used to resolve a URI (as a string) against the + * URI for the current working directory. + * + * @param uri + * the uri to resolve + * @return the resolved URI + * @throws URISyntaxException + * if the provided URI is not a valid URI + */ @NonNull protected static URI resolveAgainstCWD(@NonNull String uri) throws URISyntaxException { return UriUtils.toUri(uri, ObjectUtils.notNull(getCurrentWorkingDirectory().toUri())); diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandService.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandService.java index dfbc135c1..abd873232 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandService.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/CommandService.java @@ -5,6 +5,8 @@ package gov.nist.secauto.metaschema.cli.processor.command; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + import java.util.List; import java.util.ServiceLoader; import java.util.ServiceLoader.Provider; @@ -13,8 +15,16 @@ import edu.umd.cs.findbugs.annotations.NonNull; import nl.talsmasoftware.lazy4j.Lazy; +/** + * A service that loads commands using SPI. + *

+ * This class implements the singleton pattern to ensure a single instance of + * the command service is used throughout the application. + * + * @see ServiceLoader for more information + */ public final class CommandService { - private static final Lazy INSTANCE = Lazy.lazy(() -> new CommandService()); + private static final Lazy INSTANCE = Lazy.lazy(CommandService::new); @NonNull private final ServiceLoader loader; @@ -27,7 +37,14 @@ public static CommandService getInstance() { return INSTANCE.get(); } - public CommandService() { + /** + * Construct a new service. + *

+ * Initializes the ServiceLoader for ICommand implementations. + *

+ * This constructor is private to enforce the singleton pattern. + */ + private CommandService() { ServiceLoader loader = ServiceLoader.load(ICommand.class); assert loader != null; this.loader = loader; @@ -43,11 +60,15 @@ private ServiceLoader getLoader() { return loader; } - @SuppressWarnings("null") + /** + * Get the loaded commands. + * + * @return the list of loaded commands + */ @NonNull public List getCommands() { - return getLoader().stream() + return ObjectUtils.notNull(getLoader().stream() .map(Provider::get) - .collect(Collectors.toUnmodifiableList()); + .collect(Collectors.toUnmodifiableList())); } } 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 deleted file mode 100644 index 5149cc7a0..000000000 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/DefaultExtraArgument.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -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"); - } - this.name = name; - this.required = required; - this.number = number; - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean isRequired() { - return required; - } - - @Override - public int getNumber() { - return number; - } - -} diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ExtraArgument.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ExtraArgument.java index c7ff59180..4d0933532 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ExtraArgument.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ExtraArgument.java @@ -5,15 +5,52 @@ package gov.nist.secauto.metaschema.cli.processor.command; +import gov.nist.secauto.metaschema.cli.processor.command.impl.DefaultExtraArgument; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A representation of an extra, non-option command line argument. + */ public interface ExtraArgument { + /** + * Create a new extra argument instance. + * + * @param name + * the argument name + * @param required + * {@code true} if the argument is required, or {@code false} otherwise + * @return the instance + */ + @NonNull + static ExtraArgument newInstance(@NonNull String name, boolean required) { + if (name.isBlank()) { + throw new IllegalArgumentException("name cannot be empty or blank"); + } + return new DefaultExtraArgument(name, required); + } + + /** + * Get the argument name. + * + * @return the name + */ String getName(); + /** + * Get if the argument is required. + * + * @return {@code true} if the argument is required, or {@code false} otherwise + */ boolean isRequired(); /** - * The allowed number of arguments of this type. + * Get the allow number of arguments of this type. * - * @return a positive integer value + * @return the allowed number of arguments as a positive number or {@code -1} + * for unlimited */ - int getNumber(); + default int getNumber() { + return 1; + } } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommand.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommand.java index 854c2a1ba..4ebdd9ac9 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommand.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommand.java @@ -14,51 +14,174 @@ import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +/** + * A command line interface command. + */ public interface ICommand { + /** + * Get the name of the command. + *

+ * This name is used to call the command as a command line argument. + * + * @return the command's name + */ @NonNull String getName(); + /** + * Get a description of what the command does. + *

+ * This description is displayed in help output. + * + * @return the description + */ @NonNull String getDescription(); + /** + * Get the non-option arguments. + * + * @return the arguments, or an empty list if there are no arguments + */ @NonNull default List getExtraArguments() { return CollectionUtil.emptyList(); } - default int requiredExtraArgumentsCount() { - return (int) getExtraArguments().stream() - .filter(ExtraArgument::isRequired) - .count(); - } - + /** + * Used to gather options directly associated with this command. + * + * @return the options + */ @NonNull default Collection gatherOptions() { // by default there are no options to handle return CollectionUtil.emptyList(); } + /** + * Get any sub-commands associated with this command. + * + * @return the sub-commands + */ @NonNull - Collection getSubCommands(); - - boolean isSubCommandRequired(); + default Collection getSubCommands() { + // no sub-commands by default + return CollectionUtil.emptyList(); + } - @SuppressWarnings("unused") + /** + * Get a sub-command by it's command name. + * + * @param name + * the requested sub-command name + * @return the command or {@code null} if no sub-command exists with that name + */ + @Nullable default ICommand getSubCommandByName(@NonNull String name) { - // no sub commands by default + // no sub-commands by default return null; } - @SuppressWarnings("unused") + /** + * Determine if this command requires the use of a sub-command. + * + * @return {@code true} if a sub-command is required or {@code false} otherwise + */ + default boolean isSubCommandRequired() { + // no sub-commands by default + return false; + } + + /** + * Validate the options provided on the command line based on what is required + * for this command. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + * @throws InvalidArgumentException + * if a problem was found while validating the options + */ default void validateOptions( @NonNull CallingContext callingContext, - @NonNull CommandLine cmdLine) throws InvalidArgumentException { + @NonNull CommandLine commandLine) throws InvalidArgumentException { // by default there are no options to handle } + /** + * Create a new executor for this command. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + * @return the executor + */ @NonNull - ICommandExecutor newExecutor(@NonNull CallingContext callingContext, @NonNull CommandLine cmdLine); + ICommandExecutor newExecutor( + @NonNull CallingContext callingContext, + @NonNull CommandLine commandLine); + + /** + * Validates that the provided extra arguments meet expectations. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + * @throws InvalidArgumentException + * if a problem was found while validating the extra arguments + */ + default void validateExtraArguments( + @NonNull CallingContext callingContext, + @NonNull CommandLine commandLine) + throws InvalidArgumentException { + + validateSubCommandRequirement(); + validateArgumentCount(commandLine); + validateRequiredArguments(commandLine); + } + + private void validateSubCommandRequirement() throws InvalidArgumentException { + if (isSubCommandRequired()) { + throw new InvalidArgumentException("Please choose a valid sub-command."); + } + } + + private void validateArgumentCount(@NonNull CommandLine commandLine) throws InvalidArgumentException { + List extraArguments = getExtraArguments(); + int maxArguments = extraArguments.size(); + List actualArgs = commandLine.getArgList(); + + if (actualArgs.size() > maxArguments) { + throw new InvalidArgumentException( + String.format("Too many extra arguments provided. Expected at most %d, but got %d.", + maxArguments, actualArgs.size())); + } + + } + + private void validateRequiredArguments(@NonNull CommandLine commandLine) throws InvalidArgumentException { + List actualArgs = commandLine.getArgList(); + List requiredExtraArguments = getExtraArguments().stream() + .filter(ExtraArgument::isRequired) + .collect(Collectors.toUnmodifiableList()); + + if (actualArgs.size() < requiredExtraArguments.size()) { + throw new InvalidArgumentException( + String.format("Missing required arguments: %s. Expected %d required arguments, but got %d.", + requiredExtraArguments.stream() + .map(arg -> "<" + arg.getName() + ">") + .collect(Collectors.joining(" ")), + requiredExtraArguments.size(), + actualArgs.size())); + } + } } diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommandExecutor.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommandExecutor.java index 342d0498a..92b3acc65 100644 --- a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommandExecutor.java +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/ICommandExecutor.java @@ -11,9 +11,33 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract base class that implements the {@link ICommandExecutor} + * interface, providing common functionality for command execution + * implementations. Concrete subclasses must implement the {@link #execute()} + * method to define specific command behavior. + */ public interface ICommandExecutor { + /** + * Execute the command operation. + * + * @throws CommandExecutionException + * if an error occurred while executing the command operation + */ void execute() throws CommandExecutionException; + /** + * Create a new command executor. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + * @param function + * a function that accepts a calling context and command line + * information + * @return the executor instance + */ @NonNull static ICommandExecutor using( @NonNull CallingContext callingContext, @@ -22,8 +46,22 @@ static ICommandExecutor using( return () -> function.execute(callingContext, commandLine); } + /** + * This functional interface represents a method that is used to execute a + * command operation. + */ @FunctionalInterface interface ExecutionFunction { + /** + * Execute a command operation. + * + * @param callingContext + * the context of the command execution + * @param commandLine + * the parsed command line details + * @throws CommandExecutionException + * if an error occurred while executing the command operation + */ void execute( @NonNull CallingContext callingContext, @NonNull CommandLine commandLine) throws CommandExecutionException; diff --git a/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/impl/DefaultExtraArgument.java b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/impl/DefaultExtraArgument.java new file mode 100644 index 000000000..d9afe6e0d --- /dev/null +++ b/cli-processor/src/main/java/gov/nist/secauto/metaschema/cli/processor/command/impl/DefaultExtraArgument.java @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.cli.processor.command.impl; + +import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A default implementation of the {@link ExtraArgument} interface that + * represents a named command-line argument which can be marked as required or + * optional. + *

+ * This implementation is used by the command processor to handle additional + * arguments that are not covered by specific command options. + */ +public class DefaultExtraArgument implements ExtraArgument { + private final String name; + private final boolean required; + + /** + * Construct a new instance. + * + * @param name + * the argument name + * @param required + * {@code true} if the argument is required, or {@code false} otherwise + */ + public DefaultExtraArgument(@NonNull String name, boolean required) { + this.name = name; + this.required = required; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isRequired() { + return required; + } +} diff --git a/cli-processor/src/main/resources/log4j2.xml b/cli-processor/src/main/resources/log4j2.xml index 758bf2a4c..99656348f 100644 --- a/cli-processor/src/main/resources/log4j2.xml +++ b/cli-processor/src/main/resources/log4j2.xml @@ -3,11 +3,16 @@ - + - + + + + + + @@ -16,7 +21,12 @@ - + + + + + + diff --git a/core/src/main/java-templates/gov/nist/secauto/metaschema/core/MetaschemaJavaVersion.java b/core/src/main/java-templates/gov/nist/secauto/metaschema/core/MetaschemaJavaVersion.java index c81c8f95e..249d760ce 100644 --- a/core/src/main/java-templates/gov/nist/secauto/metaschema/core/MetaschemaJavaVersion.java +++ b/core/src/main/java-templates/gov/nist/secauto/metaschema/core/MetaschemaJavaVersion.java @@ -3,15 +3,21 @@ import gov.nist.secauto.metaschema.core.util.IVersionInfo; +/** + * Provides version information for this library. + *

+ * This class exposes build-time metadata including version numbers, build + * timestamps, and Git repository information. + */ public class MetaschemaJavaVersion implements IVersionInfo { - public static final String NAME = "metaschema-java"; - public static final String VERSION = "${project.version}"; - public static final String BUILD_TIMESTAMP = "${timestamp}"; - public static final String COMMIT = "@git.commit.id.abbrev@"; - public static final String BRANCH = "@git.branch@"; - public static final String CLOSEST_TAG = "@git.closest.tag.name@"; - public static final String ORIGIN = "@git.remote.origin.url@"; + private static final String NAME = "metaschema-java"; + private static final String VERSION = "${project.version}"; + private static final String BUILD_TIMESTAMP = "${timestamp}"; + private static final String COMMIT = "@git.commit.id.abbrev@"; + private static final String BRANCH = "@git.branch@"; + private static final String CLOSEST_TAG = "@git.closest.tag.name@"; + private static final String ORIGIN = "@git.remote.origin.url@"; @Override public String getName() { diff --git a/core/src/main/java-templates/gov/nist/secauto/metaschema/core/model/MetaschemaVersion.java b/core/src/main/java-templates/gov/nist/secauto/metaschema/core/model/MetaschemaVersion.java index a6bb77c6c..942731fda 100644 --- a/core/src/main/java-templates/gov/nist/secauto/metaschema/core/model/MetaschemaVersion.java +++ b/core/src/main/java-templates/gov/nist/secauto/metaschema/core/model/MetaschemaVersion.java @@ -4,15 +4,18 @@ import gov.nist.secauto.metaschema.core.util.IVersionInfo; +/** + * Provides version information for the underlying Metaschema implementation used by this library. + */ public class MetaschemaVersion implements IVersionInfo { - public static final String NAME = "metaschema"; - public static final String BUILD_VERSION = "${project.version}"; - public static final String BUILD_TIMESTAMP = "${timestamp}"; - public static final String COMMIT = "@metaschema-git.commit.id.abbrev@"; - public static final String BRANCH = "@metaschema-git.branch@"; - public static final String CLOSEST_TAG = "@metaschema-git.closest.tag.name@"; - public static final String ORIGIN = "@metaschema-git.remote.origin.url@"; + private static final String NAME = "metaschema"; + private static final String BUILD_VERSION = "${project.version}"; + private static final String BUILD_TIMESTAMP = "${timestamp}"; + private static final String COMMIT = "@metaschema-git.commit.id.abbrev@"; + private static final String BRANCH = "@metaschema-git.branch@"; + private static final String CLOSEST_TAG = "@metaschema-git.closest.tag.name@"; + private static final String ORIGIN = "@metaschema-git.remote.origin.url@"; @Override public String getName() { @@ -21,7 +24,7 @@ public String getName() { @Override public String getVersion() { - return CLOSEST_TAG; + return BUILD_VERSION; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/MetaschemaConstants.java b/core/src/main/java/gov/nist/secauto/metaschema/core/MetaschemaConstants.java index 0456bd903..43ad55978 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/MetaschemaConstants.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/MetaschemaConstants.java @@ -11,6 +11,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A collection of constant values related to Metaschema handling. + */ public final class MetaschemaConstants { /** * This is the namespace used by Metaschema in formats that require or use a 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 409cf786a..b5e81cdc9 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 @@ -43,5 +43,4 @@ public TYPE copy(Object obj) { // method. return ((TYPE) obj).copy(); } - } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeAdapter.java index 8f6fd6178..25f306bfc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/AbstractDataTypeAdapter.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; -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.model.util.JsonUtil; import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; @@ -197,46 +196,4 @@ public void writeJsonValue(Object value, JsonGenerator generator) throws IOExcep @Override public abstract ITEM_TYPE newItem(Object value); - - @SuppressWarnings("unchecked") - @Override - public ITEM_TYPE cast(IAnyAtomicItem item) { - if (item == null) { - throw new InvalidValueForCastFunctionException("item is null"); - } - return getItemClass().isAssignableFrom(item.getClass()) - ? (ITEM_TYPE) item - : castInternal(item); - } - - /** - * Attempt to cast the provided item to this adapter's item type. - *

- * The default implementation of this will attempt to parse the provided item as - * a string using the {@link #parse(String)} method. If this behavior is - * undesirable, then a subclass should override this method. - * - * @param item - * the item to cast - * @return the item casted to this adapter's item type - * @throws InvalidValueForCastFunctionException - * if the casting of the item is not possible because the item - * represents an invalid value for this adapter's item type - */ - @NonNull - protected ITEM_TYPE castInternal(@NonNull IAnyAtomicItem item) { - // try string based casting as a fallback - String itemString; - try { - itemString = item.asString(); - TYPE value = parse(itemString); - return newItem(value); - } catch (IllegalArgumentException | IllegalStateException ex) { - throw new InvalidValueForCastFunctionException( - String.format("The value '%s' is not compatible with the type '%s'", - item.getValue(), - getItemClass().getName()), - ex); - } - } } 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 83a8183e9..2fb732997 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 @@ -9,7 +9,6 @@ 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; @@ -137,25 +136,9 @@ default boolean isAtomic() { * the item's value * @return a new item */ - // TODO: markup types are not atomic values. - // Figure out a better base type (i.e., IValuedItem) - // TODO: move to IAnyAtomicItem @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 - */ - // TODO: move to IAnyAtomicItem - @NonNull - IAnyAtomicItem cast(IAnyAtomicItem item); - /** * Determines if adapter can parse the next element. The next element's * {@link QName} is provided for this purpose. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/AbstractIntegerAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/AbstractIntegerAdapter.java index 7dd52be02..721fcfadc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/AbstractIntegerAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/AbstractIntegerAdapter.java @@ -39,7 +39,11 @@ public JsonFormatTypes getJsonRawType() { @Override public BigInteger parse(String value) { - return new BigInteger(value); + try { + return new BigInteger(value); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException(ex); + } } @Override 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 7bcef4b6f..8c4772744 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 @@ -20,6 +20,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema base64 + * data type. + */ public class Base64Adapter extends AbstractDataTypeAdapter { @NonNull @@ -43,30 +48,39 @@ 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); + return ObjectUtils.notNull(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(); + ByteBuffer clone = buffer.isDirect() + ? ByteBuffer.allocateDirect(buffer.capacity()) + : ByteBuffer.allocate(buffer.capacity()); + ByteBuffer readOnlyCopy = buffer.asReadOnlyBuffer(); readOnlyCopy.flip(); clone.put(readOnlyCopy); return clone; } - @SuppressWarnings("null") @Override public String asString(Object value) { + ByteBuffer buffer = (ByteBuffer) value; + byte[] array; + if (buffer.hasArray()) { + array = buffer.array(); + } else { + // Handle direct buffers + array = new byte[buffer.remaining()]; + buffer.get(array); + } + Base64.Encoder encoder = Base64.getEncoder(); - return encoder.encodeToString(((ByteBuffer) value).array()); + return ObjectUtils.notNull(encoder.encodeToString(array)); } @Override 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 b0336a405..f39cd0363 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 @@ -11,11 +11,7 @@ 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; @@ -92,57 +88,4 @@ public IBooleanItem newItem(Object 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 { - try { - retval = castToBoolean(item.asStringItem()); - } catch (IllegalStateException ex) { - throw new InvalidValueForCastFunctionException(ex.getLocalizedMessage(), ex); - } - } - 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 d8191f8be..ab2c79799 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 @@ -8,14 +8,9 @@ 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.datatype.object.AmbiguousDate; 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; @@ -32,21 +27,28 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema date + * data type. + */ public class DateAdapter - extends AbstractCustomJavaDataTypeAdapter { + 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})?$"); + @NonNull + private static final Pattern DATE_TIMEZONE = ObjectUtils.notNull( + 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); + super(AmbiguousDate.class); } @Override @@ -60,7 +62,7 @@ public JsonFormatTypes getJsonRawType() { } @Override - public Date parse(String value) { + public AmbiguousDate parse(String value) { Matcher matcher = DATE_TIMEZONE.matcher(value); if (!matcher.matches()) { throw new IllegalArgumentException("Invalid date: " + value); @@ -70,12 +72,12 @@ public Date parse(String value) { = 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 + return new AmbiguousDate(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); + return new AmbiguousDate(ObjectUtils.notNull(ZonedDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC)), false); } catch (DateTimeParseException ex2) { IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); newEx.addSuppressed(ex); @@ -86,20 +88,10 @@ public Date parse(String value) { @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; + AmbiguousDate value = (AmbiguousDate) obj; + return ObjectUtils.notNull(value.hasTimeZone() + ? DateFormats.DATE_WITH_TZ.format(value.getValue()) + : DateFormats.DATE_WITHOUT_TZ.format(value.getValue())); } @Override @@ -109,23 +101,7 @@ public Class getItemClass() { @Override public IDateItem newItem(Object value) { - Date item = toValue(value); + AmbiguousDate item = toValue(value); return IDateItem.valueOf(item); } - - @Override - @NonNull - protected 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 32cb0b385..6d61a4e55 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 @@ -8,14 +8,9 @@ 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.datatype.object.AmbiguousDateTime; 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; @@ -28,8 +23,13 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema date-time + * data type. + */ public class DateTimeAdapter - extends AbstractCustomJavaDataTypeAdapter { + extends AbstractCustomJavaDataTypeAdapter { @NonNull private static final List NAMES = ObjectUtils.notNull( List.of( @@ -38,7 +38,7 @@ public class DateTimeAdapter new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "dateTime"))); DateTimeAdapter() { - super(DateTime.class); + super(AmbiguousDateTime.class); } @Override @@ -51,39 +51,44 @@ public JsonFormatTypes getJsonRawType() { return JsonFormatTypes.STRING; } - @SuppressWarnings("null") @Override - public DateTime parse(String value) { + public AmbiguousDateTime parse(String value) { + AmbiguousDateTime retval; try { - return new DateTime(ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value)), true); // NOPMD - readability + retval = parseWithTimeZone(value); } catch (DateTimeParseException ex) { try { - LocalDateTime dateTime = LocalDateTime.from(DateFormats.DATE_TIME_WITHOUT_TZ.parse(value)); - return new DateTime(ZonedDateTime.of(dateTime, ZoneOffset.UTC), false); + retval = parseWithoutTimeZone(value); } catch (DateTimeParseException ex2) { IllegalArgumentException newEx = new IllegalArgumentException(ex2.getLocalizedMessage(), ex2); newEx.addSuppressed(ex); - throw newEx; // NOPMD - it's ok + throw newEx; } } + return retval; + } + + @NonNull + private static AmbiguousDateTime parseWithTimeZone(@NonNull String value) { + return new AmbiguousDateTime( + ObjectUtils.notNull(ZonedDateTime.from(DateFormats.DATE_TIME_WITH_TZ.parse(value))), + true); + } + + @NonNull + private static AmbiguousDateTime parseWithoutTimeZone(@NonNull String value) { + LocalDateTime dateTime = LocalDateTime.from(DateFormats.DATE_TIME_WITHOUT_TZ.parse(value)); + return new AmbiguousDateTime( + ObjectUtils.notNull(ZonedDateTime.of(dateTime, ZoneOffset.UTC)), + false); } @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; + AmbiguousDateTime value = (AmbiguousDateTime) obj; + return ObjectUtils.notNull(value.hasTimeZone() + ? DateFormats.DATE_TIME_WITH_TZ.format(value.getValue()) + : DateFormats.DATE_TIME_WITHOUT_TZ.format(value.getValue())); } @Override @@ -93,23 +98,7 @@ public Class getItemClass() { @Override public IDateTimeItem newItem(Object value) { - DateTime item = toValue(value); + AmbiguousDateTime 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 d928d0bae..3c1c0f4d9 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 @@ -21,6 +21,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema date-time-with-timezone + * data type. + */ public class DateTimeWithTZAdapter extends AbstractDataTypeAdapter { @NonNull 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 5e16e7854..f309d20d7 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 @@ -23,6 +23,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema date-with-timezone + * data type. + */ public class DateWithTZAdapter extends AbstractDataTypeAdapter { @NonNull 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 5f9ca4348..6632ab70b 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 @@ -20,6 +20,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema day-time-duration + * data type. + */ public class DayTimeAdapter extends AbstractDataTypeAdapter { @NonNull 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 52169723e..25667eb66 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 @@ -10,10 +10,7 @@ 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; @@ -25,13 +22,14 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema decimal + * data type. + */ 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"); + private static final MathContext MATH_CONTEXT = MathContext.DECIMAL64; @NonNull private static final List NAMES = ObjectUtils.notNull( List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "decimal"))); @@ -80,18 +78,4 @@ 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 982325aec..35b128459 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 @@ -15,6 +15,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema email-address + * data type. + */ public class EmailAddressAdapter extends AbstractStringAdapter { @NonNull 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 51feb78d7..ab79caf20 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 @@ -15,6 +15,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema hostname + * data type. + */ public class HostnameAdapter extends AbstractStringAdapter { @NonNull 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 123a1ab27..d62023fe5 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 @@ -23,6 +23,11 @@ import inet.ipaddr.IncompatibleAddressException; import inet.ipaddr.ipv4.IPv4Address; +/** + * Support for the Metaschema ip-v4-address + * data type. + */ public class IPv4AddressAdapter extends AbstractDataTypeAdapter { @NonNull 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 324927f63..d7642d6cd 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 @@ -23,6 +23,11 @@ import inet.ipaddr.IncompatibleAddressException; import inet.ipaddr.ipv6.IPv6Address; +/** + * Support for the Metaschema ip-v6-address + * data type. + */ public class IPv6AddressAdapter extends AbstractDataTypeAdapter { @NonNull 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 f77e74630..b6634c120 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 @@ -6,10 +6,7 @@ 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; @@ -19,6 +16,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema integer + * data type. + */ public class IntegerAdapter extends AbstractIntegerAdapter { @NonNull @@ -45,18 +47,4 @@ 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/MetaschemaDataTypeProvider.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/MetaschemaDataTypeProvider.java index 6969a6cd7..bb91ba4f8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/MetaschemaDataTypeProvider.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/adapter/MetaschemaDataTypeProvider.java @@ -16,55 +16,165 @@ @SuppressWarnings("PMD.CouplingBetweenObjects") public final class MetaschemaDataTypeProvider // NOPMD - Used for service initialization extends AbstractDataTypeProvider { + /** + * The Metaschema base64 + * data type instance. + */ @NonNull public static final Base64Adapter BASE64 = new Base64Adapter(); + /** + * The Metaschema boolean + * data type instance. + */ @NonNull public static final BooleanAdapter BOOLEAN = new BooleanAdapter(); + /** + * The Metaschema date + * data type instance. + */ @NonNull public static final DateAdapter DATE = new DateAdapter(); + /** + * The Metaschema date-with-timezone + * data type instance. + */ @NonNull public static final DateWithTZAdapter DATE_WITH_TZ = new DateWithTZAdapter(); + /** + * The Metaschema date-time + * data type instance. + */ @NonNull public static final DateTimeAdapter DATE_TIME = new DateTimeAdapter(); + /** + * The Metaschema date-time-with-timezone + * data type instance. + */ @NonNull public static final DateTimeWithTZAdapter DATE_TIME_WITH_TZ = new DateTimeWithTZAdapter(); + /** + * The Metaschema ip-v4-address + * data type instance. + */ @NonNull public static final IPv4AddressAdapter IP_V4_ADDRESS = new IPv4AddressAdapter(); + /** + * The Metaschema ip-v6-address + * data type instance. + */ @NonNull public static final IPv6AddressAdapter IP_V6_ADDRESS = new IPv6AddressAdapter(); + /** + * The Metaschema uri data + * type instance. + */ @NonNull public static final UriAdapter URI = new UriAdapter(); + /** + * The Metaschema uri-reference + * data type instance. + */ @NonNull public static final UriReferenceAdapter URI_REFERENCE = new UriReferenceAdapter(); + /** + * The Metaschema uuid + * data type instance. + */ @NonNull public static final UuidAdapter UUID = new UuidAdapter(); - + /** + * The Metaschema day-time-duration + * data type instance. + */ @NonNull public static final DayTimeAdapter DAY_TIME_DURATION = new DayTimeAdapter(); + /** + * The Metaschema year-month-duration + * data type instance. + */ @NonNull public static final YearMonthAdapter YEAR_MONTH_DURATION = new YearMonthAdapter(); - + /** + * The Metaschema decimal + * data type instance. + */ @NonNull public static final DecimalAdapter DECIMAL = new DecimalAdapter(); + /** + * The Metaschema integer + * data type instance. + */ @NonNull public static final IntegerAdapter INTEGER = new IntegerAdapter(); + /** + * The Metaschema non-negative-integer + * data type instance. + */ @NonNull public static final NonNegativeIntegerAdapter NON_NEGATIVE_INTEGER = new NonNegativeIntegerAdapter(); + /** + * The Metaschema positive-integer + * data type instance. + */ @NonNull public static final PositiveIntegerAdapter POSITIVE_INTEGER = new PositiveIntegerAdapter(); - + /** + * The Metaschema email-address + * data type instance. + */ @NonNull public static final EmailAddressAdapter EMAIL_ADDRESS = new EmailAddressAdapter(); + /** + * The Metaschema hostname + * data type instance. + */ @NonNull public static final HostnameAdapter HOSTNAME = new HostnameAdapter(); + /** + * The Metaschema ncname + * data type instance. + */ @Deprecated(since = "0.7.0") @NonNull public static final NcNameAdapter NCNAME = new NcNameAdapter(); + /** + * The Metaschema string + * data type instance. + */ @NonNull public static final StringAdapter STRING = new StringAdapter(); + /** + * The Metaschema token + * data type instance. + */ @NonNull public static final TokenAdapter TOKEN = new TokenAdapter(); - + /** + * The default Metaschema data type instance to use when no data type is defined + * on a field or flag. + */ @NonNull public static final StringAdapter DEFAULT_DATA_TYPE = STRING; 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 088a4d9da..143c98077 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 @@ -15,6 +15,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema ncname + * data type. + */ @Deprecated(since = "0.7.0") public class NcNameAdapter extends AbstractStringAdapter { 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 15de6ff0a..9063bb00a 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 @@ -16,6 +16,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema non-negative-integer + * data type. + */ public class NonNegativeIntegerAdapter extends AbstractIntegerAdapter { @NonNull 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 22db33056..a78141ed7 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 @@ -16,6 +16,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema positive-integer + * data type. + */ public class PositiveIntegerAdapter extends AbstractIntegerAdapter { @NonNull 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 18b0e4d05..8c5373d74 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 @@ -15,6 +15,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema string + * data type. + */ public class StringAdapter extends AbstractStringAdapter { @NonNull 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 39091f4d5..f7d9d42a6 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 @@ -15,6 +15,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema token + * data type. + */ public class TokenAdapter extends AbstractStringAdapter { @NonNull 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 0ffb1afcc..fbeaa5137 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 @@ -19,6 +19,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema uri data + * type. + */ public class UriAdapter extends AbstractDataTypeAdapter { @NonNull 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 e51a4c6bd..92587d733 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 @@ -19,6 +19,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema uri-reference + * data type. + */ public class UriReferenceAdapter extends AbstractDataTypeAdapter { @NonNull 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 4edaface0..33313265b 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 @@ -20,11 +20,20 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema uuid + * data type. + */ public class UuidAdapter extends AbstractDataTypeAdapter { @NonNull private static final List NAMES = ObjectUtils.notNull( List.of(new QName(MetapathConstants.NS_METAPATH.toASCIIString(), "uuid"))); + + /** + * A regular expression that matches a valid 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}$"); 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 518b70ead..7f1511adf 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 @@ -20,6 +20,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Support for the Metaschema year-month-duration + * data type. + */ public class YearMonthAdapter extends AbstractDataTypeAdapter { @NonNull diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupAdapter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupAdapter.java index e745ea7d3..b4f63bd14 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupAdapter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/AbstractMarkupAdapter.java @@ -24,7 +24,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public abstract class AbstractMarkupAdapter> +/** + * Provides base support for the Metaschema markup + * data types. + */ +abstract class AbstractMarkupAdapter> extends AbstractCustomJavaDataTypeAdapter { /** @@ -47,7 +52,6 @@ public boolean isXmlMixed() { return true; } - // TODO: verify that read/write methods cannot be generalized in the base class @Override public void writeXmlValue( Object value, 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 473629e4b..c529aae06 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 @@ -14,15 +14,15 @@ import com.vladsch.flexmark.util.ast.Node; import com.vladsch.flexmark.util.ast.TextCollectingVisitor; -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.datatype.markup.flexmark.impl.AstCollectingVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.IMarkupVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.IMarkupWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.MarkupVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.MarkupXmlEventWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.MarkupXmlStreamWriter; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.apache.logging.log4j.LogManager; @@ -52,6 +52,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * The common base for all markup implementations. + * + * @param + * the Java type of the concrete markup implementation + */ @SuppressWarnings("PMD.CouplingBetweenObjects") public abstract class AbstractMarkupString> implements IMarkupString { 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 a7e961c99..8e9a06479 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 @@ -26,6 +26,12 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * The common interface for all markup implementations. + * + * @param + * the Java type of the concrete markup implementation + */ public interface IMarkupString> extends ICustomJavaDataType { /** @@ -51,22 +57,6 @@ public interface IMarkupString> */ boolean isEmpty(); - // /** - // * Write HTML content to the provided {@code xmlStreamWriter} using the - // provided {@code - // namespace}. - // * - // * @param writer - // * the writer - // * @param namespace - // * the XML namespace for the HTML - // * @throws XMLStreamException - // * if an error occurred while writing - // */ - // void writeHtml(@NonNull XMLStreamWriter2 writer, @NonNull String namespace) - // throws - // XMLStreamException; - /** * Get the HyperText Markup Language (HTML) representation of this markup * string. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupDataTypeProvider.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupDataTypeProvider.java index e2836d3ba..8750f4086 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupDataTypeProvider.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupDataTypeProvider.java @@ -15,8 +15,18 @@ */ public final class MarkupDataTypeProvider extends AbstractDataTypeProvider { + /** + * The Metaschema markup-line + * data type instance. + */ @NonNull public static final MarkupLineAdapter MARKUP_LINE = new MarkupLineAdapter(); + /** + * The Metaschema markup-multiline + * data type instance. + */ @NonNull public static final MarkupMultilineAdapter MARKUP_MULTILINE = new MarkupMultilineAdapter(); 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 605e95371..ee8ebaa79 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 @@ -14,9 +14,9 @@ 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 gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.FlexmarkConfiguration; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.SuppressPTagExtension; import java.util.Collection; import java.util.LinkedList; @@ -24,6 +24,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Supports a data value which is a single line of markup. + *

+ * This markup can be presented as XHTML or Markdown. + */ public final class MarkupLine extends AbstractMarkupString { @@ -43,7 +48,7 @@ private static DataSet newParserOptions() { options.set(Parser.LIST_BLOCK_PARSER, false); options.set(HtmlRenderer.SUPPRESS_HTML_BLOCKS, true); - Collection currentExtensions = Parser.EXTENSIONS.get(FlexmarkConfiguration.FLEXMARK_CONFIG); + Collection currentExtensions = Parser.EXTENSIONS.get(FlexmarkConfiguration.instance()); List extensions = new LinkedList<>(currentExtensions); extensions.add(SuppressPTagExtension.newInstance()); Parser.EXTENSIONS.set(options, extensions); 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 595668519..6bf2c2452 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 @@ -7,7 +7,6 @@ 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; 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 f84205316..08bf7afa8 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 @@ -11,6 +11,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Supports a data value which may be multiple lines of markup. + *

+ * This markup can be presented as XHTML or Markdown. + */ public class MarkupMultiline extends AbstractMarkupString { 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 560f0e1dc..08850f89b 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 @@ -7,7 +7,6 @@ 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; 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/XmlMarkupParser.java similarity index 97% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/XmlMarkupParser.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/XmlMarkupParser.java index 6289f6a63..b11747b8e 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/XmlMarkupParser.java @@ -3,12 +3,10 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup; 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; 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 3d8a44947..1570187b8 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 @@ -12,6 +12,9 @@ import com.vladsch.flexmark.parser.Parser; import com.vladsch.flexmark.util.data.DataHolder; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.FixedEmphasisDelimiterProcessor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.FlexmarkConfiguration; + import edu.umd.cs.findbugs.annotations.NonNull; /** @@ -58,7 +61,7 @@ public static FlexmarkFactory newInstance(@NonNull DataHolder config) { } private FlexmarkFactory() { - this(FlexmarkConfiguration.FLEXMARK_CONFIG); + this(FlexmarkConfiguration.instance()); } @SuppressWarnings("null") 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 bba9165b8..8c464fbee 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 @@ -36,6 +36,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Adds support for the use of "q" tags in HTML to replace quotation marks. + * These are translated to double quotes in Markdown. + */ public class HtmlQuoteTagExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension, FlexmarkHtmlConverter.HtmlConverterExtension { @@ -77,8 +81,8 @@ public void extend(FlexmarkHtmlConverter.Builder builder) { static class QTagNodeRenderer implements NodeRenderer { @Override - public @Nullable - Set> getNodeRenderingHandlers() { + @Nullable + public Set> getNodeRenderingHandlers() { return Collections.singleton( new NodeRenderingHandler<>(DoubleQuoteNode.class, this::render)); } @@ -139,7 +143,9 @@ public Set> getHtmlNodeRendererHandlers() { return Collections.singleton(new HtmlNodeRendererHandler<>("q", Element.class, this::renderMarkdown)); } - protected void renderMarkdown(Element element, HtmlNodeConverterContext context, + protected void renderMarkdown( + Element element, + HtmlNodeConverterContext context, @SuppressWarnings("unused") HtmlMarkdownWriter out) { context.wrapTextNodes(element, "\"", element.nextElementSibling() != null); } @@ -154,7 +160,10 @@ public HtmlNodeRenderer apply(DataHolder options) { } - public static class DoubleQuoteNode + /** + * A Flexmark node implementation representing a quotation mark. + */ + public static final class DoubleQuoteNode extends TypographicQuotes { /** @@ -163,7 +172,6 @@ public static class DoubleQuoteNode * @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/InsertAnchorExtension.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/InsertAnchorExtension.java index cf12d9c35..bf0afd03e 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 @@ -50,12 +50,16 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Extension that adds support for insert anchors, used in OSCAL statements, and + * applicable more generally in other Metaschema-based models. + */ public class InsertAnchorExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension, Formatter.FormatterExtension, FlexmarkHtmlConverter.HtmlConverterExtension { - public static final DataKey ENABLE_INLINE_INSERT_ANCHORS + private 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); + private static final DataKey ENABLE_RENDERING = new DataKey<>("ENABLE_RENDERING", true); /** * Construct a new extension instance. @@ -276,6 +280,9 @@ public HtmlNodeRenderer apply(DataHolder options) { } } + /** + * The flexmark node for an insert anchor. + */ public static class InsertAnchorNode extends Node { @@ -343,9 +350,7 @@ public void setIdReference(@NonNull BasedSequence idReference) { @Override @NonNull public BasedSequence[] getSegments() { - @NonNull - BasedSequence[] retval = { getType(), getIdReference() }; - return retval; + return new BasedSequence[] { getType(), getIdReference() }; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java index 76a8a950e..6b1867198 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AbstractMarkupWriter.java @@ -43,7 +43,6 @@ 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; 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/impl/AstCollectingVisitor.java similarity index 87% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/AstCollectingVisitor.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/AstCollectingVisitor.java index c97b017b2..451c2e8db 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/impl/AstCollectingVisitor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.util.ast.Node; import com.vladsch.flexmark.util.ast.NodeVisitorBase; @@ -12,9 +12,13 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * This class supports building a text-based representation of a Markup node + * tree. + */ public final class AstCollectingVisitor extends NodeVisitorBase { - public static final String EOL = "\n"; + private static final String LINE_SEPARATOR = System.lineSeparator(); @SuppressWarnings("PMD.AvoidStringBufferField") // short lived private final StringBuilder strBuilder; @@ -55,7 +59,7 @@ protected void visit(Node node) { assert node != null; appendIndent(); node.astString(strBuilder, true); - strBuilder.append(EOL); + strBuilder.append(LINE_SEPARATOR); indent++; try { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FixedEmphasisDelimiterProcessor.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/FixedEmphasisDelimiterProcessor.java similarity index 95% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FixedEmphasisDelimiterProcessor.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/FixedEmphasisDelimiterProcessor.java index 946d697cc..096edb6b7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FixedEmphasisDelimiterProcessor.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/FixedEmphasisDelimiterProcessor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ast.Emphasis; import com.vladsch.flexmark.ast.StrongEmphasis; @@ -17,7 +17,9 @@ /** * Provides a temporary fix for the broken {@link EmphasisDelimiterProcessor} in - * Flexmark. + * Flexmark. See the + * GitHub pull + * request. */ public class FixedEmphasisDelimiterProcessor extends AsteriskDelimiterProcessor { 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/impl/FlexmarkConfiguration.java similarity index 87% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/FlexmarkConfiguration.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/FlexmarkConfiguration.java index 52013359d..bbfa1ed24 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/impl/FlexmarkConfiguration.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ext.escaped.character.EscapedCharacterExtension; import com.vladsch.flexmark.ext.gfm.strikethrough.SubscriptExtension; @@ -21,20 +21,43 @@ import com.vladsch.flexmark.util.format.options.ListBulletMarker; import com.vladsch.flexmark.util.misc.Extension; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.HtmlQuoteTagExtension; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.InsertAnchorExtension; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; + import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import nl.talsmasoftware.lazy4j.Lazy; +/** + * This class manages a shared Flexmark configuration for use by other + * implementations. + */ public final class FlexmarkConfiguration { @NonNull private static final ParserEmulationProfile PARSER_PROFILE = ParserEmulationProfile.COMMONMARK_0_29; + /** + * The shared Flexmark configuration. + */ @NonNull - public static final DataSet FLEXMARK_CONFIG = initFlexmarkConfig(); + private static final Lazy FLEXMARK_CONFIG + = ObjectUtils.notNull(Lazy.lazy(FlexmarkConfiguration::initFlexmarkConfig)); + + /** + * Get the singleton configuration instance. + * + * @return the configuration + */ + @NonNull + public static DataSet instance() { + return ObjectUtils.notNull(FLEXMARK_CONFIG.get()); + } @SuppressWarnings("null") @NonNull @@ -133,7 +156,7 @@ private static DataSet initFlexmarkConfig() { * @return the configuration */ public static DataSet newFlexmarkConfig(@Nullable DataHolder options) { - return options == null ? FLEXMARK_CONFIG : DataSet.merge(FLEXMARK_CONFIG, options); + return options == null ? instance() : DataSet.merge(instance(), options); } private FlexmarkConfiguration() { 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/impl/HtmlCodeRenderExtension.java similarity index 89% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/HtmlCodeRenderExtension.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/HtmlCodeRenderExtension.java index ae356bc38..f098e96df 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/impl/HtmlCodeRenderExtension.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ast.Code; import com.vladsch.flexmark.ast.Text; @@ -32,6 +32,12 @@ import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * A Flexmark HTML renderer extension that customizes the rendering of code + * blocks within Metaschema documents. This implementation provides special + * handling for source position tracking and custom HTML tags while maintaining + * standard Markdown code block semantics. + */ public class HtmlCodeRenderExtension implements HtmlRenderer.HtmlRendererExtension { private static final Pattern EOL_PATTERN = Pattern.compile("\r\n|\r|\n"); @@ -79,12 +85,10 @@ private void render( // NOPMD actually used in lambda boolean customTag = htmlOptions.codeStyleHtmlOpen != null || htmlOptions.codeStyleHtmlClose != null; if (customTag) { html.raw(ObjectUtils.notNull(htmlOptions.codeStyleHtmlOpen)); + } else if (context.getHtmlOptions().sourcePositionParagraphLines) { + html.withAttr().tag("code"); } else { - if (context.getHtmlOptions().sourcePositionParagraphLines) { - html.withAttr().tag("code"); - } else { - html.srcPos(node.getText()).withAttr().tag("code"); - } + html.srcPos(node.getText()).withAttr().tag("code"); } if (codeSoftLineBreaks && !htmlOptions.isSoftBreakAllSpaces) { 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/impl/IMarkupVisitor.java similarity index 76% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupVisitor.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/IMarkupVisitor.java index e20741280..68fd00585 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/impl/IMarkupVisitor.java @@ -3,13 +3,22 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.util.ast.Document; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * Support for visiting a Flexmark syntax tree. + * + * @param + * the Java type of sync to write to + * @param + * the Java type of exception that can be thrown when a writing error + * occurs + */ @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_THROWABLE") public interface IMarkupVisitor { /** 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/impl/IMarkupWriter.java similarity index 97% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/IMarkupWriter.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/IMarkupWriter.java index df01dc58f..377927487 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/impl/IMarkupWriter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ast.AutoLink; import com.vladsch.flexmark.ast.BlockQuote; @@ -40,6 +40,15 @@ import edu.umd.cs.findbugs.annotations.Nullable; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +/** + * A common interface for writing markup to some form of an output sync. + * + * @param + * the Java type of sync to write to + * @param + * the Java type of exception that can be thrown when a writing error + * occurs + */ @SuppressFBWarnings(value = "THROWS_METHOD_THROWS_CLAUSE_THROWABLE", justification = "There is a need to support varying exceptions from multiple stream writers") public interface IMarkupWriter { // NOPMD 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/impl/MarkupVisitor.java similarity index 99% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupVisitor.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/MarkupVisitor.java index 5cd0891a3..c16759cea 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/impl/MarkupVisitor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ast.AutoLink; import com.vladsch.flexmark.ast.BlockQuote; 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/impl/MarkupXmlEventWriter.java similarity index 97% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlEventWriter.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/MarkupXmlEventWriter.java index 006a30a4f..94336e025 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/impl/MarkupXmlEventWriter.java @@ -3,11 +3,10 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; 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; @@ -27,6 +26,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Provides support for writing Flexmark markup nodes to an XML event stream. + */ public class MarkupXmlEventWriter extends AbstractMarkupWriter { 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/impl/MarkupXmlStreamWriter.java similarity index 79% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupXmlStreamWriter.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/MarkupXmlStreamWriter.java index 86831d0bd..752c04caf 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/impl/MarkupXmlStreamWriter.java @@ -3,12 +3,10 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; 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; @@ -17,7 +15,24 @@ import edu.umd.cs.findbugs.annotations.NonNull; -// TODO: Is this orphaned code needed? +/** + * Provides support for writing Flexmark markup nodes to an XML stream. This + * implementation uses {@link XMLStreamWriter} to write markup elements and + * their attributes in a streaming fashion. + *

+ * This class is not thread-safe and should not be shared between threads + * without external synchronization. + *

+ * Example usage: + * + *

{@code
+ * XMLStreamWriter writer = ...;
+ * MarkupXmlStreamWriter markupWriter = new MarkupXmlStreamWriter(
+ *     "http://example.com/ns",
+ *     ListOptions.getDefaultListOptions(),
+ *     writer);
+ * }
+ */ public class MarkupXmlStreamWriter extends AbstractMarkupWriter { 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/impl/SuppressPTagExtension.java similarity index 94% rename from core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/SuppressPTagExtension.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/impl/SuppressPTagExtension.java index 683c75720..4e146c379 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/impl/SuppressPTagExtension.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; +package gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.html.HtmlRenderer; @@ -21,6 +21,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +/** + * Provides the ability to suppress paragraph "p" tags for single line markup + * generation. + */ public class SuppressPTagExtension implements HtmlRenderer.HtmlRendererExtension { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AbstractAmbiguousTemporal.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AbstractAmbiguousTemporal.java index 303ad3600..4f89018ff 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AbstractAmbiguousTemporal.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AbstractAmbiguousTemporal.java @@ -12,6 +12,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** + * Implementations of this class represent a temporal value which may not have a + * timezone making it ambiguous as a point/window in time. + *

* Metaschema has a need to represent dates and times that allow for an * ambiguous time zone. This is due to some models not requiring a time zone as * part of a date/time. An ambiguous dateTime allows a time zone to be inferred, @@ -22,7 +25,9 @@ * written back out in such cases. * * @param - * the bound object type + * the bound object type that extends this class, used for proper type + * inheritance in implementing classes like {@code AmbiguousDate} or + * {@code AmbiguousDateTime} */ public abstract class AbstractAmbiguousTemporal> extends AbstractCustomJavaDataType { @@ -52,4 +57,10 @@ public AbstractAmbiguousTemporal(@NonNull ZonedDateTime value, boolean hasTimeZo public boolean hasTimeZone() { return timeZone; } + + @Override + public String toString() { + return getValue().toString() + (hasTimeZone() ? "" : "(abiguous)"); + } + } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDate.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDate.java new file mode 100644 index 000000000..d00ef1522 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDate.java @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.datatype.object; + +import java.time.ZonedDateTime; +import java.util.Objects; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a date value which may not have a timezone making it ambiguous as + * a window in time. + */ +public class AmbiguousDate + extends AbstractAmbiguousTemporal { + + /** + * Construct a new date object. This type supports ambiguous dates that were + * provided without a time zone. + *

+ * The date value will be ambiguous if the {@code hasTimeZone} is {@code false}. + * + * @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 AmbiguousDate(@NonNull ZonedDateTime value, boolean hasTimeZone) { + super(value, hasTimeZone); + } + + @Override + public AmbiguousDate copy() { + return new AmbiguousDate(getValue(), hasTimeZone()); + } + + @Override + public int hashCode() { + return Objects.hash(getValue(), hasTimeZone()); + } + + @SuppressWarnings("PMD.OnlyOneReturn") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AmbiguousDate)) { + return false; + } + AmbiguousDate other = (AmbiguousDate) obj; + return hasTimeZone() == other.hasTimeZone() && getValue().equals(other.getValue()); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDateTime.java b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDateTime.java new file mode 100644 index 000000000..99dfd9ac6 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/AmbiguousDateTime.java @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.datatype.object; + +import java.time.ZonedDateTime; +import java.util.Objects; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a date/time value which may not have a timezone making it + * ambiguous as a point in time. + */ +public class AmbiguousDateTime + extends AbstractAmbiguousTemporal { + + /** + * Construct a new date/time object. This type supports ambiguous dates/times + * that were provided without a time zone. + *

+ * The date/time value will be ambiguous if the {@code hasTimeZone} is + * {@code false}. + * + * @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 AmbiguousDateTime(@NonNull ZonedDateTime value, boolean hasTimeZone) { + super(value, hasTimeZone); + } + + @Override + public AmbiguousDateTime copy() { + return new AmbiguousDateTime(getValue(), hasTimeZone()); + } + + @Override + public int hashCode() { + return Objects.hash(getValue(), hasTimeZone()); + } + + @SuppressWarnings("PMD.OnlyOneReturn") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AmbiguousDateTime)) { + return false; + } + AmbiguousDateTime other = (AmbiguousDateTime) obj; + return hasTimeZone() == other.hasTimeZone() && getValue().equals(other.getValue()); + } +} 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 deleted file mode 100644 index 6a26460ef..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/Date.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -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 deleted file mode 100644 index 197f7ecce..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/datatype/object/DateTime.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -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/EQNameUtils.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/EQNameUtils.java index e62fb0a00..82b173318 100644 --- 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 @@ -13,6 +13,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Utilities for managing qualified names. + */ public final class EQNameUtils { private static final Pattern URI_QUALIFIED_NAME = Pattern.compile("^Q\\{([^{}]*)\\}(.+)$"); private static final Pattern LEXICAL_NAME = Pattern.compile("^(?:([^:]+):)?(.+)$"); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java index 834276250..6f6b5dfd6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/ICollectionValue.java @@ -12,6 +12,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A data value that can be a value in a Metapath array or map. + */ public interface ICollectionValue { /** * Get the collection value as a sequence. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java index 7ff7c7b87..cb63888fc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/IPrintable.java @@ -7,6 +7,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Identifies the implementation as being able to produce a string value for + * output. + */ public interface IPrintable { /** * Get the string value. 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 6dc3652d9..d389ec1e6 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 @@ -9,48 +9,98 @@ import java.net.URI; -import javax.xml.XMLConstants; - import edu.umd.cs.findbugs.annotations.NonNull; /** - * Provides constant values used in Metapath. + * Provides constant values for use in Metapath. */ @SuppressWarnings("PMD.DataClass") public final class MetapathConstants { + /** + * The namespace URI for Metapath data types and built-in casting functions. + */ @NonNull public static final URI NS_METAPATH = ObjectUtils.requireNonNull( URI.create("http://csrc.nist.gov/ns/metaschema/metapath")); - @NonNull - public static final URI NS_XML_SCHEMA = ObjectUtils.requireNonNull( - URI.create(XMLConstants.W3C_XML_SCHEMA_NS_URI)); + /** + * The namespace URI for Metapath built-in functions. + * + * @see #PREFIX_METAPATH for the default prefix for this namespace + */ @NonNull public static final URI NS_METAPATH_FUNCTIONS = ObjectUtils.requireNonNull( URI.create("http://csrc.nist.gov/ns/metaschema/metapath-functions")); + /** + * The namespace URI for Metapath math-related built-in functions. + * + * @see #PREFIX_METAPATH_FUNCTIONS_MATH for the default prefix for this + * namespace + */ @NonNull public static final URI NS_METAPATH_FUNCTIONS_MATH = ObjectUtils.requireNonNull( URI.create(NS_METAPATH_FUNCTIONS + "/math")); + /** + * The namespace URI for Metapath array-related built-in functions. + * + * @see #PREFIX_METAPATH_FUNCTIONS_ARRAY for the default prefix for this + * namespace + */ @NonNull public static final URI NS_METAPATH_FUNCTIONS_ARRAY = ObjectUtils.requireNonNull( URI.create(NS_METAPATH_FUNCTIONS + "/array")); + /** + * The namespace URI for Metapath map-related built-in functions. + * + * @see #PREFIX_METAPATH_FUNCTIONS_MAP for the default prefix for this namespace + */ @NonNull public static final URI NS_METAPATH_FUNCTIONS_MAP = ObjectUtils.requireNonNull( URI.create(NS_METAPATH_FUNCTIONS + "/map")); + /** + * The namespace URI for Metapath extension built-in functions. + *

+ * This is currently an alias for {@link #NS_METAPATH_FUNCTIONS} and can be used + * when implementing custom extension functions to distinguish them from core + * functions. + */ @NonNull public static final URI NS_METAPATH_FUNCTIONS_EXTENDED = NS_METAPATH_FUNCTIONS; + /** + * The namespace prefix for Metapath data types and built-in casting functions. + * + * @see #NS_METAPATH for the corresponding namespace URI + */ @NonNull public static final String PREFIX_METAPATH = "meta"; + /** + * The namespace prefix for Metapath built-in functions. + * + * @see #NS_METAPATH_FUNCTIONS for the corresponding namespace URI + */ @NonNull - public static final String PREFIX_XML_SCHEMA = "xs"; - @NonNull - public static final String PREFIX_XPATH_FUNCTIONS = "mp"; + public static final String PREFIX_METAPATH_FUNCTIONS = "mp"; + /** + * The namespace prefix for Metapath math-related built-in functions. + * + * @see #NS_METAPATH_FUNCTIONS_MATH for the corresponding namespace URI + */ @NonNull - public static final String PREFIX_XPATH_FUNCTIONS_MATH = "math"; + public static final String PREFIX_METAPATH_FUNCTIONS_MATH = "math"; + /** + * The namespace prefix for Metapath array-related built-in functions. + * + * @see #NS_METAPATH_FUNCTIONS_ARRAY for the corresponding namespace URI + */ @NonNull - public static final String PREFIX_XPATH_FUNCTIONS_ARRAY = "array"; + public static final String PREFIX_METAPATH_FUNCTIONS_ARRAY = "array"; + /** + * The namespace prefix for Metapath map-related built-in functions. + * + * @see #NS_METAPATH_FUNCTIONS_MAP for the corresponding namespace URI + */ @NonNull - public static final String PREFIX_XPATH_FUNCTIONS_MAP = "map"; + public static final String PREFIX_METAPATH_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 2cdcfdaed..a7f931347 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 @@ -18,7 +18,6 @@ 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.atomic.IDecimalItem; import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -48,34 +47,90 @@ }) public class MetapathExpression { + /** + * Identifies the expected type for a Metapath evaluation result. + */ public enum ResultType { /** * The result is expected to be a {@link BigDecimal} value. */ - NUMBER, + NUMBER(BigDecimal.class, sequence -> { + INumericItem numeric = FunctionUtils.toNumeric(sequence, true); + return numeric == null ? null : numeric.asDecimal(); + }), /** * The result is expected to be a {@link String} value. */ - STRING, + STRING(String.class, sequence -> { + IAnyAtomicItem item = FnData.fnData(sequence).getFirstItem(true); + return item == null ? "" : item.asString(); + }), /** * The result is expected to be a {@link Boolean} value. */ - BOOLEAN, + BOOLEAN(Boolean.class, sequence -> FnBoolean.fnBoolean(sequence).toBoolean()), /** * The result is expected to be an {@link ISequence} value. */ - SEQUENCE, + SEQUENCE(ISequence.class, sequence -> sequence), /** * The result is expected to be an {@link IItem} value. */ - ITEM; - } + ITEM(IItem.class, sequence -> sequence.getFirstItem(true)); - private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class); + @NonNull + private final Class clazz; + private final ConversionFunction converter; + + ResultType(@NonNull Class clazz, @NonNull ConversionFunction converter) { + this.clazz = clazz; + this.converter = converter; + } + + /** + * Get the expected class for the result type. + * + * @return the expected class + * + */ + @NonNull + public Class expectedClass() { + return clazz; + } + /** + * Convert the provided sequence to the expected type. + * + * @param + * the Java type of the expected return value + * @param sequence + * the Metapath result sequence to convert + * @return the converted sequence as the expected type + * @throws TypeMetapathException + * if the provided sequence is incompatible with the expected result + * type + */ + @Nullable + public T convert(@NonNull ISequence sequence) { + try { + return ObjectUtils.asNullableType(converter.convert(sequence)); + } catch (ClassCastException ex) { + throw new InvalidTypeMetapathException(null, + String.format("Unable to cast to expected result type '%s' using expected type '%s'.", + name(), + expectedClass().getName()), + ex); + } + } + } + + /** + * The Metapath expression identifying the current context node. + */ @NonNull public static final MetapathExpression CONTEXT_NODE = new MetapathExpression(".", ContextItem.instance(), StaticContext.instance()); + private static final Logger LOGGER = LogManager.getLogger(MetapathExpression.class); @NonNull private final String path; @@ -231,7 +286,7 @@ public String toString() { * type * @throws MetapathException * if an error occurred during evaluation - * @see #toResultType(ISequence, ResultType) + * @see ResultType#convert(ISequence) */ @Nullable public T evaluateAs(@NonNull ResultType resultType) { @@ -255,14 +310,14 @@ public T evaluateAs(@NonNull ResultType resultType) { * type * @throws MetapathException * if an error occurred during evaluation - * @see #toResultType(ISequence, ResultType) + * @see ResultType#convert(ISequence) */ @Nullable public T evaluateAs( @Nullable IItem focus, @NonNull ResultType resultType) { ISequence result = evaluate(focus); - return toResultType(result, resultType); + return resultType.convert(result); } /** @@ -286,7 +341,7 @@ public T evaluateAs( * type * @throws MetapathException * if an error occurred during evaluation - * @see #toResultType(ISequence, ResultType) + * @see ResultType#convert(ISequence) */ @Nullable public T evaluateAs( @@ -294,62 +349,7 @@ public T evaluateAs( @NonNull ResultType resultType, @NonNull DynamicContext dynamicContext) { ISequence result = evaluate(focus, dynamicContext); - return toResultType(result, resultType); - } - - /** - * Converts the provided {@code sequence} to the requested {@code resultType}. - *

- * The {@code resultType} determines the returned result, which is derived from - * the evaluation result sequence, as follows: - *

    - *
  • BOOLEAN - the effective boolean result is produced using - * {@link FnBoolean#fnBoolean(ISequence)}.
  • - *
  • NODE - the first result item in the sequence is returned.
  • - *
  • NUMBER - the sequence is cast to a number using - * {@link IDecimalItem#cast(IAnyAtomicItem)}.
  • - *
  • SEQUENCE - the evaluation result sequence.
  • - *
  • STRING - the string value of the first result item in the sequence.
  • - *
- * - * @param - * the requested return value - * @param sequence - * the sequence to convert - * @param resultType - * the type of result to produce - * @return the converted result - * @throws TypeMetapathException - * if the provided sequence is incompatible with the requested result - * type - */ - @SuppressWarnings({ "PMD.NullAssignment", "PMD.CyclomaticComplexity" }) // for readability - @Nullable - protected T toResultType(@NonNull ISequence sequence, @NonNull ResultType resultType) { - Object result; - switch (resultType) { - case BOOLEAN: - result = FnBoolean.fnBoolean(sequence).toBoolean(); - break; - case ITEM: - result = sequence.getFirstItem(true); - break; - case NUMBER: - INumericItem numeric = FunctionUtils.toNumeric(sequence, true); - result = numeric == null ? null : numeric.asDecimal(); - break; - case SEQUENCE: - result = sequence; - break; - case STRING: - 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())); - } - - return ObjectUtils.asNullableType(result); + return resultType.convert(result); } /** @@ -419,4 +419,10 @@ public ISequence evaluate( ex); } } + + @FunctionalInterface + interface ConversionFunction { + @Nullable + Object convert(@NonNull ISequence sequence); + } } 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 13bce12ee..626db41a1 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 @@ -37,19 +37,16 @@ public final class StaticContext { MetapathConstants.PREFIX_METAPATH, MetapathConstants.NS_METAPATH); knownNamespaces.put( - MetapathConstants.PREFIX_XML_SCHEMA, - MetapathConstants.NS_XML_SCHEMA); - knownNamespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS, + MetapathConstants.PREFIX_METAPATH_FUNCTIONS, MetapathConstants.NS_METAPATH_FUNCTIONS); knownNamespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS_MATH, + MetapathConstants.PREFIX_METAPATH_FUNCTIONS_MATH, MetapathConstants.NS_METAPATH_FUNCTIONS_MATH); knownNamespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS_ARRAY, + MetapathConstants.PREFIX_METAPATH_FUNCTIONS_ARRAY, MetapathConstants.NS_METAPATH_FUNCTIONS_ARRAY); knownNamespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS_MAP, + MetapathConstants.PREFIX_METAPATH_FUNCTIONS_MAP, MetapathConstants.NS_METAPATH_FUNCTIONS_MAP); WELL_KNOWN_NAMESPACES = CollectionUtil.unmodifiableMap(knownNamespaces); @@ -362,7 +359,7 @@ public static Builder builder() { * A builder used to generate the static context. */ public static final class Builder { - public boolean useWildcardWhenNamespaceNotDefaulted; // false + private boolean useWildcardWhenNamespaceNotDefaulted; // false @Nullable private URI baseUri; @NonNull @@ -373,18 +370,7 @@ public static final class Builder { private URI defaultFunctionNamespace = MetapathConstants.NS_METAPATH_FUNCTIONS; private Builder() { - namespaces.put( - MetapathConstants.PREFIX_METAPATH, - MetapathConstants.NS_METAPATH); - namespaces.put( - MetapathConstants.PREFIX_XML_SCHEMA, - MetapathConstants.NS_XML_SCHEMA); - namespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS, - MetapathConstants.NS_METAPATH_FUNCTIONS); - namespaces.put( - MetapathConstants.PREFIX_XPATH_FUNCTIONS_MATH, - MetapathConstants.NS_METAPATH_FUNCTIONS_MATH); + // avoid direct construction } /** 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 1b682f269..50c59be96 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 @@ -79,8 +79,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; -public abstract class AbstractAstVisitor // NOPMD +/** + * This abstract class supports processing an ANTLR-based abstract syntax tree + * by walking the tree using a visitor pattern. + * + * @param + * the Java type of the result produced through visitation + */ +@SuppressWarnings({ "PMD.ExcessivePublicCount", "PMD.CyclomaticComplexity" }) +public abstract class AbstractAstVisitor extends Metapath10BaseVisitor { + private static final String ERR_NO_DELEGATION + = "This method should never be called directly as it is handled by the parent expression."; + private static final String ERR_SINGLE_CHILD = "A single child expression was expected."; /** * This dispatch method will call the node handler on a leaf node or if multiple @@ -125,7 +136,7 @@ protected R delegateToChild(@NonNull T ctx) { if (ctx.getChildCount() == 1) { return ctx.getChild(0).accept(this); } - throw new IllegalStateException("a single child expression was expected"); + throw new IllegalStateException(ERR_SINGLE_CHILD); } // ============================================================ @@ -150,7 +161,7 @@ public R visitMetapath(MetapathContext ctx) { @Override public R visitExpr(ExprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleExpr(ctx)); + return handle(ctx, this::handleExpr); } @Override @@ -185,7 +196,7 @@ public R visitPrimaryexpr(PrimaryexprContext ctx) { @Override public R visitLiteral(LiteralContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleStringLiteral(ctx)); + return handle(ctx, this::handleStringLiteral); } /** @@ -200,7 +211,7 @@ public R visitLiteral(LiteralContext ctx) { @Override public R visitNumericliteral(NumericliteralContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleNumericLiteral(ctx)); + return handle(ctx, this::handleNumericLiteral); } // ================================================================== @@ -266,7 +277,7 @@ public R visitParenthesizedexpr(ParenthesizedexprContext ctx) { @Override public R visitContextitemexpr(ContextitemexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleContextitemexpr(ctx)); + return handle(ctx, this::handleContextitemexpr); } // ========================================================================= @@ -285,19 +296,19 @@ public R visitContextitemexpr(ContextitemexprContext ctx) { @Override public R visitFunctioncall(FunctioncallContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleFunctioncall(ctx)); + return handle(ctx, this::handleFunctioncall); } @Override public R visitArgumentlist(ArgumentlistContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitArgument(ArgumentContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // ======================================================================= @@ -326,7 +337,7 @@ public R visitEnclosedexpr(EnclosedexprContext ctx) { @Override public R visitPostfixexpr(PostfixexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handlePostfixexpr(ctx)); + return handle(ctx, this::handlePostfixexpr); } /** @@ -375,7 +386,7 @@ public R visitLookup(LookupContext ctx) { @Override public R visitPathexpr(PathexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handlePathexpr(ctx)); + return handle(ctx, this::handlePathexpr); } // ============================================================ @@ -395,7 +406,7 @@ public R visitPathexpr(PathexprContext ctx) { @Override public R visitRelativepathexpr(RelativepathexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleRelativepathexpr(ctx)); + return handle(ctx, this::handleRelativepathexpr); } // ================================================ @@ -421,7 +432,7 @@ public R visitStepexpr(StepexprContext ctx) { public R visitForwardstep(ForwardstepContext ctx) { assert ctx != null; // this will either call the handler or forward for AbbrevforwardstepContext - return handle(ctx, (context) -> handleForwardstep(ctx)); + return handle(ctx, this::handleForwardstep); } /** @@ -437,7 +448,7 @@ public R visitForwardstep(ForwardstepContext ctx) { public R visitReversestep(ReversestepContext ctx) { assert ctx != null; // this will either call the handler or forward for AbbrevreversestepContext - return handle(ctx, (context) -> handleReversestep(ctx)); + return handle(ctx, this::handleReversestep); } // ====================================================================== @@ -456,13 +467,13 @@ public R visitReversestep(ReversestepContext ctx) { @Override public R visitAxisstep(AxisstepContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleAxisstep(ctx)); + return handle(ctx, this::handleAxisstep); } @Override public R visitPredicatelist(PredicatelistContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // =========================================== @@ -472,13 +483,13 @@ public R visitPredicatelist(PredicatelistContext ctx) { @Override public R visitForwardaxis(ForwardaxisContext ctx) { // should never be called, since this is handled by handleForwardstep - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitReverseaxis(ReverseaxisContext ctx) { // should never be called, since this is handled by handleReversestep - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // ======================================================= @@ -488,19 +499,19 @@ public R visitReverseaxis(ReverseaxisContext ctx) { @Override public R visitNodetest(NodetestContext ctx) { // should never be called, since this is handled by the calling context - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitNametest(NametestContext ctx) { // should never be called, since this is handled by the calling context - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitEqname(EqnameContext ctx) { // should never be called, since this is handled by the calling context - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } /** @@ -569,7 +580,7 @@ public R visitAbbrevreversestep(AbbrevreversestepContext ctx) { @Override public R visitRangeexpr(RangeexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleRangeexpr(ctx)); + return handle(ctx, this::handleRangeexpr); } // ======================================================================== @@ -588,7 +599,7 @@ public R visitRangeexpr(RangeexprContext ctx) { @Override public R visitUnionexpr(UnionexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleUnionexpr(ctx)); + return handle(ctx, this::handleUnionexpr); } /** @@ -603,7 +614,7 @@ public R visitUnionexpr(UnionexprContext ctx) { @Override public R visitIntersectexceptexpr(IntersectexceptexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleIntersectexceptexpr(ctx)); + return handle(ctx, this::handleIntersectexceptexpr); } // ====================================================================== @@ -622,7 +633,7 @@ public R visitIntersectexceptexpr(IntersectexceptexprContext ctx) { @Override public R visitAdditiveexpr(AdditiveexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleAdditiveexpr(ctx)); + return handle(ctx, this::handleAdditiveexpr); } /** @@ -637,7 +648,7 @@ public R visitAdditiveexpr(AdditiveexprContext ctx) { @Override public R visitMultiplicativeexpr(MultiplicativeexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleMultiplicativeexpr(ctx)); + return handle(ctx, this::handleMultiplicativeexpr); } /** @@ -652,7 +663,7 @@ public R visitMultiplicativeexpr(MultiplicativeexprContext ctx) { @Override public R visitUnaryexpr(UnaryexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleUnaryexpr(ctx)); + return handle(ctx, this::handleUnaryexpr); } @Override @@ -678,7 +689,7 @@ public R visitValueexpr(ValueexprContext ctx) { @Override public R visitStringconcatexpr(StringconcatexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleStringconcatexpr(ctx)); + return handle(ctx, this::handleStringconcatexpr); } // ======================================================================= @@ -697,19 +708,19 @@ public R visitStringconcatexpr(StringconcatexprContext ctx) { @Override public R visitComparisonexpr(ComparisonexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleComparisonexpr(ctx)); + return handle(ctx, this::handleComparisonexpr); } @Override public R visitValuecomp(ValuecompContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitGeneralcomp(GeneralcompContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // ============================================================================ @@ -728,7 +739,7 @@ public R visitGeneralcomp(GeneralcompContext ctx) { @Override public R visitOrexpr(OrexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleOrexpr(ctx)); + return handle(ctx, this::handleOrexpr); } /** @@ -743,7 +754,7 @@ public R visitOrexpr(OrexprContext ctx) { @Override public R visitAndexpr(AndexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleAndexpr(ctx)); + return handle(ctx, this::handleAndexpr); } // ==================================================================== @@ -762,19 +773,19 @@ public R visitAndexpr(AndexprContext ctx) { @Override public R visitForexpr(ForexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleForexpr(ctx)); + return handle(ctx, this::handleForexpr); } @Override public R visitSimpleforclause(SimpleforclauseContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitSimpleforbinding(SimpleforbindingContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // ==================================================================== @@ -799,13 +810,13 @@ public R visitLetexpr(LetexprContext ctx) { @Override public R visitSimpleletclause(SimpleletclauseContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override public R visitSimpleletbinding(SimpleletbindingContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } // ====================================================================== @@ -830,7 +841,7 @@ public R visitMapconstructor(MapconstructorContext ctx) { @Override public R visitMapconstructorentry(MapconstructorentryContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } @Override @@ -888,7 +899,7 @@ public R visitCurlyarrayconstructor(CurlyarrayconstructorContext ctx) { @Override public R visitKeyspecifier(KeyspecifierContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } /** @@ -922,7 +933,7 @@ public R visitUnarylookup(UnarylookupContext ctx) { @Override public R visitIfexpr(IfexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleIfexpr(ctx)); + return handle(ctx, this::handleIfexpr); } /* @@ -966,7 +977,7 @@ public R visitQuantifiedexpr(QuantifiedexprContext ctx) { @Override public R visitSimplemapexpr(SimplemapexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleSimplemapexpr(ctx)); + return handle(ctx, this::handleSimplemapexpr); } /* @@ -987,12 +998,12 @@ public R visitSimplemapexpr(SimplemapexprContext ctx) { @Override public R visitArrowexpr(ArrowexprContext ctx) { assert ctx != null; - return handle(ctx, (context) -> handleArrowexpr(ctx)); + return handle(ctx, this::handleArrowexpr); } @Override public R visitArrowfunctionspecifier(ArrowfunctionspecifierContext ctx) { // should never be called, since this is handled by the parent expression - throw new IllegalStateException(); + throw new IllegalStateException(ERR_NO_DELEGATION); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java index aea605851..94dcb57c4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/FailingErrorListener.java @@ -10,6 +10,13 @@ import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.misc.ParseCancellationException; +/** + * An ANTLR error listener that throws a {@link ParseCancellationException} when + * a syntax error is found. + *

+ * The exception message contains details around the line and character position + * where the error occurred. + */ public class FailingErrorListener extends BaseErrorListener { @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/Metapath10ParserBase.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/Metapath10ParserBase.java index 44f8661d6..8931064ef 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/Metapath10ParserBase.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/Metapath10ParserBase.java @@ -8,8 +8,40 @@ import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.TokenStream; +import java.util.Set; + +/** + * Base class for Metapath ANTLR-based parsing operations. This class extends + * the ANTLR Parser to provide specialized processing for Metapath expressions. + *

+ * ANTLR (ANother Tool for Language Recognition) is used here to generate the + * parser for Metapath expressions. This base class provides common + * functionality that can be used by the generated parser implementation. + *

+ * Implementations should extend this class to define specific parsing rules and + * behaviors for Metapath processing. + */ public abstract class Metapath10ParserBase extends Parser { + + private static final Set KEYWORD_TOKENS = Set.of( + Metapath10.KW_ARRAY, + Metapath10.KW_FLAG, + Metapath10.KW_COMMENT, + Metapath10.KW_DOCUMENT_NODE, + Metapath10.KW_ELEMENT, + Metapath10.KW_EMPTY_SEQUENCE, + Metapath10.KW_FUNCTION, + Metapath10.KW_IF, + Metapath10.KW_ITEM, + Metapath10.KW_MAP, + Metapath10.KW_NAMESPACE_NODE, + Metapath10.KW_NODE, + Metapath10.KW_PROCESSING_INSTRUCTION, + Metapath10.KW_SCHEMA_ATTRIBUTE, + Metapath10.KW_SCHEMA_ELEMENT, + Metapath10.KW_TEXT); + /** * Construct a new parser base. * @@ -27,21 +59,7 @@ protected Metapath10ParserBase(TokenStream input) { * {@code false} otherwise */ protected boolean isFuncCall() { - return !(getInputStream().LA(1) == Metapath10.KW_ARRAY - || getInputStream().LA(1) == Metapath10.KW_FLAG - || getInputStream().LA(1) == Metapath10.KW_COMMENT - || getInputStream().LA(1) == Metapath10.KW_DOCUMENT_NODE - || getInputStream().LA(1) == Metapath10.KW_ELEMENT - || getInputStream().LA(1) == Metapath10.KW_EMPTY_SEQUENCE - || getInputStream().LA(1) == Metapath10.KW_FUNCTION - || getInputStream().LA(1) == Metapath10.KW_IF - || getInputStream().LA(1) == Metapath10.KW_ITEM - || getInputStream().LA(1) == Metapath10.KW_MAP - || getInputStream().LA(1) == Metapath10.KW_NAMESPACE_NODE - || getInputStream().LA(1) == Metapath10.KW_NODE - || getInputStream().LA(1) == Metapath10.KW_PROCESSING_INSTRUCTION - || getInputStream().LA(1) == Metapath10.KW_SCHEMA_ATTRIBUTE - || getInputStream().LA(1) == Metapath10.KW_SCHEMA_ELEMENT - || getInputStream().LA(1) == Metapath10.KW_TEXT); + int nextToken = getInputStream().LA(1); + return !KEYWORD_TOKENS.contains(nextToken); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/ParseTreePrinter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/ParseTreePrinter.java index b70e28cec..1cf327c21 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/ParseTreePrinter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/antlr/ParseTreePrinter.java @@ -14,6 +14,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Supports walking an ANTLR parse tree to generate a textual representation of + * the tree. + */ public class ParseTreePrinter { @SuppressWarnings("resource") @NotOwning 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 aab9f3737..4d53bb5cc 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 @@ -147,10 +147,10 @@ private static IAnyAtomicItem applyGeneralComparisonCast(@NonNull IAnyAtomicItem retval = IDecimalItem.cast(other); } else if (item instanceof IDayTimeDurationItem) { retval = IDayTimeDurationItem.cast(other); - } else if (item instanceof IDayTimeDurationItem) { + } else if (item instanceof IYearMonthDurationItem) { retval = IYearMonthDurationItem.cast(other); } else { - retval = item.getJavaTypeAdapter().cast(other); + retval = item.castAsType(other); } return retval; } 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 a968762bb..b7cb48fa6 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 @@ -127,6 +127,9 @@ public static INumericItem toNumeric(@NonNull ISequence sequence, boolean req public static INumericItem toNumeric(@NonNull IItem item) { // atomize IAnyAtomicItem atomicItem = ISequence.getFirstItem(FnData.atomize(item), true); + if (atomicItem == null) { + throw new InvalidTypeMetapathException(item, "Unable to cast null item"); + } return toNumeric(atomicItem); } @@ -140,7 +143,7 @@ public static INumericItem toNumeric(@NonNull IItem item) { * if the item cannot be cast to a numeric value */ @NonNull - public static INumericItem toNumeric(@Nullable IAnyAtomicItem item) { + public static INumericItem toNumeric(@NonNull IAnyAtomicItem item) { try { return IDecimalItem.cast(item); } catch (InvalidValueForCastFunctionException ex) { @@ -237,7 +240,8 @@ public static TYPE requireType(Class clazz, IItem ite null, String.format("Expected non-null type '%s', but the node was null.", clazz.getName())); - } else if (!clazz.isInstance(item)) { + } + if (!clazz.isInstance(item)) { throw new InvalidTypeMetapathException( item, String.format("Expected type '%s', but the node was type '%s'.", 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 8d75eee9c..3ee86412b 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 @@ -36,12 +36,12 @@ public final class CastFunction implements IFunctio static IFunction signature( @NonNull URI namespace, @NonNull String name, - @NonNull Class resulingAtomicType, + @NonNull Class resultingAtomicType, @NonNull ICastExecutor executor) { return signature( ObjectUtils.notNull(namespace.toASCIIString()), name, - resulingAtomicType, + resultingAtomicType, executor); } @@ -96,6 +96,12 @@ public ISequence execute(@NonNull IFunction function, return ISequence.of(castItem); } + /** + * A callback used to perform a casting operation. + * + * @param + * the Java type for the resulting item + */ @FunctionalInterface public interface ICastExecutor { /** 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 c4eab2db9..d5b984abf 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 @@ -34,6 +34,7 @@ public class DefaultFunctionLibrary /** * Initialize the built-in function library. */ + @SuppressWarnings("deprecation") public DefaultFunctionLibrary() { // NOPMD - intentional // https://www.w3.org/TR/xpath-functions-31/#func-abs registerFunction(FnAbs.SIGNATURE); 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 407634a19..99626871e 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 @@ -20,8 +20,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; /** - * Implements the XPath 3.1 - * fn:round + * Implements the XPath 3.1 fn:ceiling * function. */ public final class FnCeiling { 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 45bd024b3..afca2be5d 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 @@ -15,13 +15,19 @@ 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.IAnyUriItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBooleanItem; +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.IDecimalItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDurationItem; 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.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,7 +43,21 @@ public final class FnMinMax { private static final String NAME_MIN = "min"; private static final String NAME_MAX = "max"; - + /** + * Defines the set of primitive atomic item types supported by the min/max + * functions. This set is used for type validation and normalization during + * comparison operations. + */ + @NonNull + private static final Set> PRIMITIVE_ITEM_TYPES = ObjectUtils.notNull(Set.of( + IStringItem.class, + IBooleanItem.class, + IDecimalItem.class, + IDurationItem.class, + IDateTimeItem.class, + IDateItem.class, + IBase64BinaryItem.class, + IAnyUriItem.class)); @NonNull static final IFunction SIGNATURE_MIN = IFunction.builder() .name(NAME_MIN) @@ -149,36 +169,57 @@ private static Stream normalize( return Stream.of(items.get(0)); } - List resultingItems = ObjectUtils.notNull(items.stream() + List resultingItems = convertUntypedItems(items); + Map, Integer> counts = countItemTypes(resultingItems); + return createNormalizedStream(resultingItems, counts); + } + + @NonNull + private static List convertUntypedItems( + @NonNull List items) { + return ObjectUtils.notNull(items.stream() .map(item -> item instanceof IUntypedAtomicItem ? IDecimalItem.cast(item) : item) .collect(Collectors.toList())); + } - Map, Integer> counts = FunctionUtils.countTypes( - IAnyAtomicItem.PRIMITIVE_ITEM_TYPES, - resultingItems); + @NonNull + private static Map, Integer> countItemTypes( + @NonNull List items) { + return FunctionUtils.countTypes(PRIMITIVE_ITEM_TYPES, items); + } - Stream stream = null; + @SuppressWarnings("PMD.OnlyOneReturn") + @NonNull + private static Stream createNormalizedStream( + @NonNull List items, + @NonNull Map, Integer> counts) { + + // Single type - no conversion needed if (counts.size() == 1) { - stream = resultingItems.stream(); - } else if (counts.size() > 1) { - int size = resultingItems.size(); + return ObjectUtils.notNull(items.stream()); + } + + // Multiple types - attempt conversion + int size = items.size(); + if (counts.size() > 1) { + // Check if all items are either String or AnyUri if (counts.getOrDefault(IStringItem.class, 0) + counts.getOrDefault(IAnyUriItem.class, 0) == size) { - stream = resultingItems.stream() - .map(IAnyAtomicItem::asStringItem); - } else if (counts.getOrDefault(IDecimalItem.class, 0) == size) { - stream = resultingItems.stream() - .map(item -> (IDecimalItem) item); + return ObjectUtils.notNull(items.stream().map(IAnyAtomicItem::asStringItem)); } - } - if (stream == null) { - throw new InvalidArgumentFunctionException( - InvalidArgumentFunctionException.INVALID_ARGUMENT_TYPE, - String.format("Values must all be of a single atomic type. Their types are '%s'.", - FunctionUtils.getTypes(resultingItems).stream() - .map(Class::getName) - .collect(Collectors.joining(",")))); + // Check if all items are Decimal + if (counts.getOrDefault(IDecimalItem.class, 0) == size) { + return ObjectUtils.notNull(items.stream().map(item -> (IDecimalItem) item)); + } } - return stream; + + // No valid conversion possible + throw new InvalidArgumentFunctionException( + InvalidArgumentFunctionException.INVALID_ARGUMENT_TYPE, + String.format( + "Values must all be of a single atomic type. Found multiple types: [%s]", + FunctionUtils.getTypes(items).stream() + .map(Class::getSimpleName) + .collect(Collectors.joining(", ")))); } } 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 index 6d0c41d9b..9500d8bb2 100644 --- 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 @@ -29,7 +29,7 @@ public final class MapEntry { private static final String NAME = "entry"; @NonNull - public static final IFunction SIGNATURE = IFunction.builder() + static final IFunction SIGNATURE = IFunction.builder() .name(NAME) .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS_MAP) .deterministic() diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAnyAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAnyAtomicItem.java index 334e0c92c..c12c9d4d2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAnyAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAnyAtomicItem.java @@ -5,18 +5,19 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; -import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import edu.umd.cs.findbugs.annotations.NonNull; /** - * Provides a common implementation for all atomic types. + * Provides a common implementation for all atomic types that have an underlying + * value. * * @param * the Java type associated with the atomic type. */ -public abstract class AbstractAnyAtomicItem implements IAnyAtomicItem { +public abstract class AbstractAnyAtomicItem + extends AbstractAtomicItemBase { @NonNull private final TYPE value; @@ -36,17 +37,4 @@ public TYPE getValue() { return value; } - @Override - @NonNull - public abstract IDataTypeAdapter getJavaTypeAdapter(); - - @Override - public String asString() { - return getJavaTypeAdapter().asString(getValue()); - } - - @Override - public String toString() { - return asString(); - } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAtomicItemBase.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAtomicItemBase.java new file mode 100644 index 000000000..c3c3c6a92 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractAtomicItemBase.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.atomic; + +import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Provides base functionality for atomic item implementations. + * + * @param + * the Java type of the underlying data value + */ +public abstract class AbstractAtomicItemBase implements IAnyAtomicItem { + + @Override + @NonNull + public abstract IDataTypeAdapter getJavaTypeAdapter(); + + @Override + public String asString() { + return getJavaTypeAdapter().asString(getValue()); + } + + @Override + public String toString() { + return asString(); + } +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithoutTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithoutTimeZoneItemImpl.java deleted file mode 100644 index 182a7bcdf..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithoutTimeZoneItemImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.metapath.item.atomic; - -import gov.nist.secauto.metaschema.core.datatype.adapter.DateTimeAdapter; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.datatype.object.DateTime; - -import java.time.ZonedDateTime; - -import edu.umd.cs.findbugs.annotations.NonNull; - -class DateTimeWithoutTimeZoneItemImpl - extends AbstractDateTimeItem { - - public DateTimeWithoutTimeZoneItemImpl(@NonNull DateTime value) { - super(value); - } - - @Override - public ZonedDateTime asZonedDateTime() { - return getValue().getValue(); - } - - @Override - public DateTimeAdapter getJavaTypeAdapter() { - return MetaschemaDataTypeProvider.DATE_TIME; - } - -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithoutTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithoutTimeZoneItemImpl.java deleted file mode 100644 index 806cee2ce..000000000 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithoutTimeZoneItemImpl.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-FileCopyrightText: none - * SPDX-License-Identifier: CC0-1.0 - */ - -package gov.nist.secauto.metaschema.core.metapath.item.atomic; - -import gov.nist.secauto.metaschema.core.datatype.adapter.DateAdapter; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.datatype.object.Date; - -import java.time.ZonedDateTime; - -import edu.umd.cs.findbugs.annotations.NonNull; - -class DateWithoutTimeZoneItemImpl - extends AbstractDateItem { - - public DateWithoutTimeZoneItemImpl(@NonNull Date value) { - super(value); - } - - @Override - public ZonedDateTime asZonedDateTime() { - return getValue().getValue(); - } - - @Override - public DateAdapter getJavaTypeAdapter() { - return MetaschemaDataTypeProvider.DATE; - } -} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java index 13f20a671..fef08f6fe 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyAtomicItem.java @@ -7,26 +7,19 @@ import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.metapath.IPrintable; +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; import gov.nist.secauto.metaschema.core.metapath.item.IItemVisitor; 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.Set; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * The interface shared by all atomic items, representing indivisible data + * values that serve as the fundamental building blocks for complex data + * structures in the Metaschema framework. + */ public interface IAnyAtomicItem extends IAtomicValuedItem, IPrintable { - @NonNull - Set> PRIMITIVE_ITEM_TYPES = ObjectUtils.notNull(Set.of( - IStringItem.class, - IBooleanItem.class, - IDecimalItem.class, - IDurationItem.class, - IDateTimeItem.class, - IDateItem.class, - IBase64BinaryItem.class, - IAnyUriItem.class)); @Override @NonNull @@ -90,7 +83,9 @@ default IStringItem asStringItem() { * * @param item * the item to cast - * @return the result from casting + * @return an atomic item of this type + * @throws InvalidValueForCastFunctionException + * if the provided item type cannot be cast to this item type */ @NonNull IAnyAtomicItem castAsType(@NonNull IAnyAtomicItem item); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyUriItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyUriItem.java index f9359fede..2521e5bdf 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyUriItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IAnyUriItem.java @@ -6,13 +6,18 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.AnyUriItemImpl; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a URI data value. + */ public interface IAnyUriItem extends IAnyAtomicItem { /** * Construct a new URI item using the provided string {@code value}. @@ -20,16 +25,32 @@ public interface IAnyUriItem extends IAnyAtomicItem { * @param value * a string representing a URI * @return the new item - * @throws IllegalArgumentException + * @throws InvalidTypeMetapathException * if the given string violates RFC2396 */ @NonNull static IAnyUriItem valueOf(@NonNull String value) { - return valueOf(ObjectUtils.notNull(URI.create(value))); + try { + return valueOf(MetaschemaDataTypeProvider.URI.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid URI value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } } /** * Construct a new URI item using the provided URI {@code value}. + *

+ * Example usage: + * + *

+   * URI uri = URI.create("http://example.com");
+   * IAnyUriItem item = IAnyUriItem.valueOf(uri);
+   * 
* * @param value * a URI @@ -59,7 +80,19 @@ static IAnyUriItem valueOf(@NonNull URI value) { */ @NonNull static IAnyUriItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.URI.cast(item); + try { + return item instanceof IAnyUriItem + ? (IAnyUriItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } + } + + @Override + default IAnyUriItem castAsType(IAnyAtomicItem item) { + return cast(item); } /** @@ -102,11 +135,6 @@ default IAnyUriItem resolve(@NonNull IAnyUriItem other) { return valueOf(ObjectUtils.notNull(asUri().resolve(other.asUri()))); } - @Override - default IAnyUriItem castAsType(IAnyAtomicItem item) { - return cast(item); - } - @Override default int compareTo(IAnyAtomicItem item) { return compareTo(cast(item)); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBase64BinaryItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBase64BinaryItem.java index a86039042..82a912079 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBase64BinaryItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBase64BinaryItem.java @@ -6,14 +6,16 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.Base64BinaryItemImpl; import java.nio.ByteBuffer; import edu.umd.cs.findbugs.annotations.NonNull; /** - * A Metapath atomic item containing a Base64 encoded data value. + * An atomic Metapath item containing a Base64 encoded data value. */ public interface IBase64BinaryItem extends IAnyAtomicItem { @@ -24,10 +26,21 @@ public interface IBase64BinaryItem extends IAnyAtomicItem { * @param value * a string representing base64 encoded data * @return the new item + * @throws InvalidTypeMetapathException + * if the provided string is not a valid Base64 character sequence */ @NonNull static IBase64BinaryItem valueOf(@NonNull String value) { - return cast(IStringItem.valueOf(value)); + try { + return valueOf(MetaschemaDataTypeProvider.BASE64.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("The value starting with '%s' is not a valid Base64 character sequence. %s", + value.substring(0, Math.min(value.length(), 200)), + ex.getLocalizedMessage()), + ex); + } } /** @@ -55,7 +68,14 @@ static IBase64BinaryItem valueOf(@NonNull ByteBuffer value) { */ @NonNull static IBase64BinaryItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.BASE64.cast(item); + try { + return item instanceof IBase64BinaryItem + ? (IBase64BinaryItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBooleanItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBooleanItem.java index 79ad9ee28..20016c0ed 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBooleanItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IBooleanItem.java @@ -6,12 +6,14 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.BooleanItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; /** - * A Metapath atomic item with a boolean value. + * An atomic Metapath item with a boolean value. */ public interface IBooleanItem extends IAnyAtomicItem { /** @@ -34,6 +36,8 @@ public interface IBooleanItem extends IAnyAtomicItem { * @param value * a string representing a boolean value * @return the new item + * @throws InvalidTypeMetapathException + * if the provided value is not a valid boolean value */ @NonNull static IBooleanItem valueOf(@NonNull String value) { @@ -42,10 +46,13 @@ static IBooleanItem valueOf(@NonNull String value) { retval = TRUE; } else { try { - Boolean bool = MetaschemaDataTypeProvider.BOOLEAN.parse(value); - retval = valueOf(bool); + retval = valueOf(MetaschemaDataTypeProvider.BOOLEAN.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid boolean value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -76,7 +83,29 @@ static IBooleanItem valueOf(boolean value) { */ @NonNull static IBooleanItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.BOOLEAN.cast(item); + IBooleanItem retval; + if (item instanceof INumericItem) { + retval = valueOf(((INumericItem) item).toEffectiveBoolean()); + } else { + try { + retval = valueOf(INumericItem.cast(item).toEffectiveBoolean()); + } catch (InvalidValueForCastFunctionException ex) { + try { + retval = valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex2) { + // asString can throw IllegalStateException exception + InvalidValueForCastFunctionException thrown = new InvalidValueForCastFunctionException(ex2); + thrown.addSuppressed(ex); + throw thrown; + } + } + } + return retval; + } + + @Override + default IBooleanItem castAsType(IAnyAtomicItem item) { + return cast(item); } /** @@ -86,11 +115,6 @@ static IBooleanItem cast(@NonNull IAnyAtomicItem item) { */ boolean toBoolean(); - @Override - default IBooleanItem castAsType(IAnyAtomicItem item) { - return cast(item); - } - /** * Get the boolean negation of this value. * @@ -103,7 +127,7 @@ default IBooleanItem negate() { @Override default int compareTo(IAnyAtomicItem item) { - return compareTo(cast(item)); + return compareTo(castAsType(item)); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateItem.java index 4223748a3..c29e38f66 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateItem.java @@ -6,15 +6,24 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.datatype.object.Date; +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDate; +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDateTime; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DateWithTimeZoneItemImpl; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DateWithoutTimeZoneItemImpl; import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a date data value. + */ public interface IDateItem extends ITemporalItem { /** @@ -29,20 +38,64 @@ static IDateItem valueOf(@NonNull String value) { try { return valueOf(MetaschemaDataTypeProvider.DATE.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), ex); + throw new InvalidTypeMetapathException( + null, + String.format("Invalid date value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); } } /** * Construct a new date item using the provided {@code value}. + *

+ * This method handles recording that the timezone is implicit. + * + * @param value + * a date, without time zone information + * @return the new item + * @see AmbiguousDateTime for more details on timezone handling + */ + @NonNull + static IDateItem valueOf(@NonNull LocalDate value) { + return valueOf(ObjectUtils.notNull(value.atStartOfDay(ZoneOffset.UTC)), false); + } + + /** + * Construct a new date item using the provided {@code value}. + *

+ * This method handles recording if an explicit timezone was provided using the + * {@code hasTimeZone} parameter. The {@link AmbiguousDate#hasTimeZone()} method + * can be called to determine if timezone information is present. * * @param value * a date, without time zone information + * @param hasTimeZone + * {@code true} if the date/time is intended to have an associated time + * zone or {@code false} otherwise * @return the new item + * @see AmbiguousDateTime for more details on timezone handling */ @NonNull - static IDateItem valueOf(@NonNull Date value) { - return new DateWithoutTimeZoneItemImpl(value); + static IDateItem valueOf(@NonNull ZonedDateTime value, boolean hasTimeZone) { + return hasTimeZone + ? valueOf(value) + : valueOf(new AmbiguousDate(value, false)); + } + + /** + * Construct a new date item using the provided {@code value}. + * + * @param value + * an ambiguous date with time zone information already identified + * @return the new item + */ + @NonNull + static IDateItem valueOf(@NonNull AmbiguousDate value) { + return value.hasTimeZone() + ? valueOf(value.getValue()) + : new DateWithoutTimeZoneItemImpl(value); } /** @@ -71,13 +124,28 @@ static IDateItem valueOf(@NonNull ZonedDateTime value) { */ @NonNull static IDateItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.DATE.cast(item); + IDateItem retval; + if (item instanceof IDateItem) { + retval = (IDateItem) item; + } else if (item instanceof IDateTimeItem) { + ZonedDateTime value = ((IDateTimeItem) item).asZonedDateTime(); + retval = valueOf(value); + } else if (item instanceof IStringItem || item instanceof IUntypedAtomicItem) { + try { + retval = valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } + } else { + throw new InvalidValueForCastFunctionException( + String.format("unsupported item type '%s'", item.getClass().getName())); + } + return retval; } @Override - default IDateItem castAsType(IAnyAtomicItem item) { - return cast(item); - } + IDateItem castAsType(IAnyAtomicItem item); @Override default int compareTo(IAnyAtomicItem item) { diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateTimeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateTimeItem.java index 40088877f..83292573d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateTimeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDateTimeItem.java @@ -6,13 +6,25 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; -import gov.nist.secauto.metaschema.core.datatype.object.DateTime; +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDateTime; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DateTimeWithTimeZoneItemImpl; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DateTimeWithoutTimeZoneItemImpl; import java.time.ZonedDateTime; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item representing a date/time value in the Metapath + * system. + *

+ * This interface provides functionality for handling date/time values with and + * without time zone information, supporting parsing, casting, and comparison + * operations. It works in conjunction with {@link AmbiguousDateTime} to + * properly handle time zone ambiguity. + */ public interface IDateTimeItem extends ITemporalItem { /** * Construct a new date/time item using the provided string {@code value}. @@ -26,25 +38,62 @@ static IDateTimeItem valueOf(@NonNull String value) { try { return valueOf(MetaschemaDataTypeProvider.DATE_TIME.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid date/time value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } /** * Construct a new date/time item using the provided {@code value}. + *

+ * This method handles recording if an explicit timezone was provided using the + * {@code hasTimeZone} parameter. The {@link AmbiguousDateTime#hasTimeZone()} + * method can be called to determine if timezone information is present. + * + * @param value + * a date/time, without time zone information + * @param hasTimeZone + * {@code true} if the date/time is intended to have an associated time + * zone or {@code false} otherwise + * @return the new item + * @see AmbiguousDateTime for more details on timezone handling + */ + @NonNull + static IDateTimeItem valueOf(@NonNull ZonedDateTime value, boolean hasTimeZone) { + return hasTimeZone + ? valueOf(value) + : valueOf(new AmbiguousDateTime(value, false)); + } + + /** + * Construct a new date/time item using the provided {@code value}. + *

+ * This method handles recording if an explicit timezone was provided using the + * {@link AmbiguousDateTime}. The {@link AmbiguousDateTime#hasTimeZone()} method + * can be called to determine if timezone information is present. * * @param value * a date/time, without time zone information * @return the new item + * @see AmbiguousDateTime for more details on timezone handling */ @NonNull - static IDateTimeItem valueOf(@NonNull DateTime value) { - return new DateTimeWithoutTimeZoneItemImpl(value); + static IDateTimeItem valueOf(@NonNull AmbiguousDateTime value) { + return value.hasTimeZone() + ? valueOf(value.getValue()) + : new DateTimeWithoutTimeZoneItemImpl(value); } /** * Construct a new date/time item using the provided {@code value}. + *

+ * This method handles dates with explicit timezone information using + * ZonedDateTime. The timezone is preserved as specified in the input and is + * significant for date/time operations and comparisons. * * @param value * a date/time, with time zone information @@ -67,7 +116,22 @@ static IDateTimeItem valueOf(@NonNull ZonedDateTime value) { */ @NonNull static IDateTimeItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.DATE_TIME.cast(item); + IDateTimeItem retval; + if (item instanceof IDateTimeItem) { + retval = (IDateTimeItem) item; + } else { + try { + retval = valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException( + String.format("The value '%s' is not compatible with the type '%s'", + item.getValue(), + item.getClass().getName()), + ex); + } + } + return retval; } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDayTimeDurationItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDayTimeDurationItem.java index 2d47ba484..42a28ecb2 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDayTimeDurationItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDayTimeDurationItem.java @@ -6,12 +6,18 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DayTimeDurationItemImpl; import java.time.Duration; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a duration data value in days, hours, and + * seconds. + */ public interface IDayTimeDurationItem extends IDurationItem { /** * Construct a new day time duration item using the provided string @@ -20,13 +26,20 @@ public interface IDayTimeDurationItem extends IDurationItem { * @param value * a string representing a day time duration * @return the new item + * @throws InvalidTypeMetapathException + * if the provided string value is not a day/time duration value + * according to ISO 8601 */ @NonNull static IDayTimeDurationItem valueOf(@NonNull String value) { try { return valueOf(MetaschemaDataTypeProvider.DAY_TIME_DURATION.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid day/time value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -55,7 +68,14 @@ static IDayTimeDurationItem valueOf(@NonNull Duration value) { */ @NonNull static IDayTimeDurationItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.DAY_TIME_DURATION.cast(item); + try { + return item instanceof IDayTimeDurationItem + ? (IDayTimeDurationItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java index b6e4e9925..a6900b323 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDecimalItem.java @@ -6,19 +6,25 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.DecimalItemImpl; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.math.BigDecimal; import java.math.RoundingMode; import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; +/** + * An atomic Metapath item containing a decimal data value. + */ public interface IDecimalItem extends INumericItem { - @SuppressWarnings("null") + /** + * The decimal item with the value "0". + */ @NonNull - IDecimalItem ZERO = valueOf(BigDecimal.ZERO); + IDecimalItem ZERO = valueOf(ObjectUtils.notNull(BigDecimal.ZERO)); /** * Construct a new decimal item using the provided string {@code value}. @@ -26,13 +32,19 @@ public interface IDecimalItem extends INumericItem { * @param value * a string representing a decimal value * @return the new item + * @throws InvalidTypeMetapathException + * if the given string is not a decimal value */ @NonNull static IDecimalItem valueOf(@NonNull String value) { try { return valueOf(MetaschemaDataTypeProvider.DECIMAL.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid decimal value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -61,6 +73,18 @@ static IDecimalItem valueOf(double value) { return valueOf(ObjectUtils.notNull(Double.toString(value))); } + /** + * Construct a new decimal item using the provided {@code value}. + * + * @param value + * a double value + * @return the new item + */ + @NonNull + static IDecimalItem valueOf(boolean value) { + return valueOf(DecimalItemImpl.toBigDecimal(value)); + } + /** * Construct a new decimal item using the provided {@code value}. * @@ -84,13 +108,28 @@ static IDecimalItem valueOf(@NonNull BigDecimal value) { * if the provided {@code item} cannot be cast to this type */ @NonNull - static IDecimalItem cast(@Nullable IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.DECIMAL.cast(item); + static IDecimalItem cast(@NonNull IAnyAtomicItem item) { + IDecimalItem retval; + if (item instanceof IDecimalItem) { + retval = (IDecimalItem) item; + } else if (item instanceof INumericItem) { + retval = valueOf(((INumericItem) item).asDecimal()); + } else if (item instanceof IBooleanItem) { + retval = valueOf(((IBooleanItem) item).toBoolean()); + } else { + try { + retval = valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } + } + return retval; } @Override default IDecimalItem castAsType(IAnyAtomicItem item) { - return valueOf(cast(item).asDecimal()); + return cast(item); } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDurationItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDurationItem.java index ce9817e24..6b0b7fc7d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDurationItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IDurationItem.java @@ -11,6 +11,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item representing a duration data value. + *

+ * This interface supports both day-time and year-month duration formats + * following the ISO 8601 standard. Examples of valid durations include: + *

    + *
  • P1Y2M (1 year, 2 months)
  • + *
  • P3DT4H5M (3 days, 4 hours, 5 minutes)
  • + *
+ * + * @see IDayTimeDurationItem + * @see IYearMonthDurationItem + */ public interface IDurationItem extends IAnyAtomicItem { /** * Cast the provided type to this item type. @@ -28,13 +41,23 @@ static IDurationItem cast(@NonNull IAnyAtomicItem item) { if (item instanceof IDurationItem) { retval = (IDurationItem) item; } else { + String value; + try { + value = item.asString(); + } catch (IllegalStateException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } + try { - retval = IDayTimeDurationItem.valueOf(item.asString()); + retval = IDayTimeDurationItem.valueOf(value); } catch (IllegalStateException ex) { try { - retval = IYearMonthDurationItem.valueOf(item.asString()); + retval = IYearMonthDurationItem.valueOf(value); } catch (IllegalStateException ex2) { - InvalidValueForCastFunctionException newEx = new InvalidValueForCastFunctionException(ex2); + InvalidValueForCastFunctionException newEx = new InvalidValueForCastFunctionException( + String.format("Value '%s' cannot be parsed as either a day-time or year-month duration", value), + ex2); newEx.addSuppressed(ex); throw newEx; // NOPMD context as suppressed } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IEmailAddressItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IEmailAddressItem.java index ddfe5d54f..bc54171fc 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IEmailAddressItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IEmailAddressItem.java @@ -6,10 +6,15 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.EmailAddressItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing an email address data value. + */ public interface IEmailAddressItem extends IStringItem { /** * Construct a new email address item using the provided string {@code value}. @@ -17,13 +22,19 @@ public interface IEmailAddressItem extends IStringItem { * @param value * a string representing an email address value * @return the new item + * @throws InvalidTypeMetapathException + * if the given string is not an email address value */ @NonNull static IEmailAddressItem valueOf(@NonNull String value) { try { return new EmailAddressItemImpl(MetaschemaDataTypeProvider.EMAIL_ADDRESS.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid email address value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -40,7 +51,14 @@ static IEmailAddressItem valueOf(@NonNull String value) { */ @NonNull static IEmailAddressItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.EMAIL_ADDRESS.cast(item); + try { + return item instanceof IEmailAddressItem + ? (IEmailAddressItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IHostnameItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IHostnameItem.java index 61fb7086a..001f1bfc4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IHostnameItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IHostnameItem.java @@ -6,16 +6,21 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.HostnameItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a hostname data value. + */ public interface IHostnameItem extends IStringItem { /** * Construct a new host name item using the provided string {@code value}. * * @param value - * a string representing an host name value + * a string representing a host name value * @return the new item */ @NonNull @@ -23,7 +28,11 @@ static IHostnameItem valueOf(@NonNull String value) { try { return new HostnameItemImpl(MetaschemaDataTypeProvider.HOSTNAME.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid hostname value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -40,7 +49,14 @@ static IHostnameItem valueOf(@NonNull String value) { */ @NonNull static IHostnameItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.HOSTNAME.cast(item); + try { + return item instanceof IHostnameItem + ? (IHostnameItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java index 687f4be5e..59dd04a23 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPAddressItem.java @@ -5,9 +5,14 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; +import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; + import edu.umd.cs.findbugs.annotations.NonNull; import inet.ipaddr.IPAddress; +/** + * An atomic Metapath item representing an IP address data value. + */ public interface IIPAddressItem extends IUntypedAtomicItem { /** * Get the "wrapped" IP address value. @@ -28,4 +33,32 @@ public interface IIPAddressItem extends IUntypedAtomicItem { default int compareTo(IIPAddressItem item) { return asIpAddress().compareTo(item.asIpAddress()); } + + /** + * Cast the provided type to this item type. + * + * @param item + * the item to cast + * @return the original item if it is already this type, otherwise a new item + * cast to this type + * @throws InvalidValueForCastFunctionException + * if the provided {@code item} cannot be cast to this type + */ + @NonNull + static IIPAddressItem cast(@NonNull IAnyAtomicItem item) { + if (!(item instanceof IIPAddressItem)) { + String value = null; + try { + value = item.asString(); + } catch (IllegalStateException ex) { + // do nothing. this is a best effort to get the value + } + + throw new InvalidValueForCastFunctionException( + String.format("The value '%s' of type '%s' is not an internet protocol address.", + value == null ? "(unknown)" : value, + item.getJavaTypeAdapter().getPreferredName())); + } + return (IIPAddressItem) item; + } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv4AddressItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv4AddressItem.java index e24c1c9d3..7de02e6f5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv4AddressItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv4AddressItem.java @@ -6,13 +6,39 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.IPv4AddressItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; import inet.ipaddr.ipv4.IPv4Address; +/** + * An atomic Metapath item containing an IPv4 address data value. + */ public interface IIPv4AddressItem extends IIPAddressItem { + /** + * Construct a new IPv4 item using the provided {@code value}. + * + * @param value + * an IPv4 value + * @return the new item + */ + @NonNull + static IIPv4AddressItem valueOf(@NonNull String value) { + try { + return valueOf(MetaschemaDataTypeProvider.IP_V4_ADDRESS.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid IPv4 address value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } + } + /** * Construct a new IPv4 item using the provided {@code value}. * @@ -37,7 +63,14 @@ static IIPv4AddressItem valueOf(@NonNull IPv4Address value) { */ @NonNull static IIPv4AddressItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.IP_V4_ADDRESS.cast(item); + try { + return item instanceof IIPv4AddressItem + ? (IIPv4AddressItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv6AddressItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv6AddressItem.java index 23edb002c..82c3234a8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv6AddressItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIPv6AddressItem.java @@ -6,13 +6,39 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.IPv6AddressItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; import inet.ipaddr.ipv6.IPv6Address; +/** + * An atomic Metapath item containing an IPv6 address data value. + */ public interface IIPv6AddressItem extends IIPAddressItem { + /** + * Construct a new IPv6 item using the provided {@code value}. + * + * @param value + * an IPv6 value + * @return the new item + */ + @NonNull + static IIPv6AddressItem valueOf(@NonNull String value) { + try { + return valueOf(MetaschemaDataTypeProvider.IP_V6_ADDRESS.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid IPv6 address value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } + } + /** * Construct a new IPv6 item using the provided {@code value}. * @@ -37,7 +63,14 @@ static IIPv6AddressItem valueOf(@NonNull IPv6Address value) { */ @NonNull static IIPv6AddressItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.IP_V6_ADDRESS.cast(item); + try { + return item instanceof IIPv6AddressItem + ? (IIPv6AddressItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java index db6b14002..8190df2eb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IIntegerItem.java @@ -5,25 +5,34 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.IntegerItemImpl; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.math.BigInteger; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing an integer data value. + */ public interface IIntegerItem extends IDecimalItem { - - @SuppressWarnings("null") + /** + * The integer value "1". + */ @NonNull - IIntegerItem ONE = valueOf(BigInteger.ONE); - @SuppressWarnings("null") + IIntegerItem ONE = valueOf(ObjectUtils.notNull(BigInteger.ONE)); + /** + * The integer value "0". + */ @NonNull - IIntegerItem ZERO = valueOf(BigInteger.ZERO); - @SuppressWarnings("null") + IIntegerItem ZERO = valueOf(ObjectUtils.notNull(BigInteger.ZERO)); + /** + * The integer value "-1". + */ @NonNull - IIntegerItem NEGATIVE_ONE = valueOf(BigInteger.ONE.negate()); + IIntegerItem NEGATIVE_ONE = valueOf(ObjectUtils.notNull(BigInteger.ONE.negate())); /** * Create an item from an existing integer value. @@ -75,6 +84,18 @@ static IIntegerItem valueOf(long value) { return valueOf(bigInteger); } + /** + * Construct a new integer item using the provided {@code value}. + * + * @param value + * a long value + * @return the new item + */ + @NonNull + static IIntegerItem valueOf(boolean value) { + return valueOf(ObjectUtils.notNull(value ? BigInteger.ONE : BigInteger.ZERO)); + } + /** * Construct a new integer item using the provided {@code value}. * @@ -109,7 +130,27 @@ static IIntegerItem valueOf(@NonNull BigInteger value) { */ @NonNull static IIntegerItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.INTEGER.cast(item); + IIntegerItem retval; + if (item instanceof IIntegerItem) { + retval = (IIntegerItem) item; + } else if (item instanceof INumericItem) { + retval = valueOf(((INumericItem) item).asInteger()); + } else if (item instanceof IBooleanItem) { + retval = valueOf(((IBooleanItem) item).toBoolean()); + } else { + try { + retval = valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } + } + return retval; + } + + @Override + default IIntegerItem castAsType(IAnyAtomicItem item) { + return cast(item); } @Override @@ -125,11 +166,6 @@ default IIntegerItem floor() { return this; } - @Override - default IIntegerItem castAsType(IAnyAtomicItem item) { - return valueOf(cast(item).asInteger()); - } - @Override default int compareTo(IAnyAtomicItem item) { return compareTo(cast(item)); diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IMarkupItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IMarkupItem.java index 22f450021..c6bbc0e17 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IMarkupItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IMarkupItem.java @@ -9,11 +9,34 @@ import gov.nist.secauto.metaschema.core.datatype.markup.MarkupDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.MarkupLineItemImpl; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.MarkupMultiLineItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item representing a Markup data value. + */ public interface IMarkupItem extends IUntypedAtomicItem { + /** + * Construct a new item using the provided {@code value}. + * + * @param value + * a line of markup + * @return the new item + */ + @SuppressWarnings("PMD.AvoidCatchingGenericException") + @NonNull + static IMarkupItem valueOf(@NonNull String value) { + try { + return valueOf(MarkupDataTypeProvider.MARKUP_LINE.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidValueForCastFunctionException(ex); + } + } + /** * Construct a new item using the provided {@code value}. * @@ -50,7 +73,14 @@ static IMarkupItem valueOf(@NonNull MarkupMultiline value) { */ @NonNull static IMarkupItem cast(@NonNull IAnyAtomicItem item) { - return MarkupDataTypeProvider.MARKUP_MULTILINE.cast(item); + try { + return item instanceof IMarkupItem + ? (IMarkupItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INcNameItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INcNameItem.java index e26b346f8..3253d8d2d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INcNameItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INcNameItem.java @@ -6,10 +6,15 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.NcNameItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a non-colonized name (NCName) data value. + */ @Deprecated(forRemoval = true, since = "0.7.0") public interface INcNameItem extends IStringItem { /** @@ -25,7 +30,11 @@ static INcNameItem valueOf(@NonNull String value) { try { return new NcNameItemImpl(MetaschemaDataTypeProvider.NCNAME.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid non-colonized name value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -42,7 +51,14 @@ static INcNameItem valueOf(@NonNull String value) { */ @NonNull static INcNameItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.NCNAME.cast(item); + try { + return item instanceof INcNameItem + ? (INcNameItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INonNegativeIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INonNegativeIntegerItem.java index b346796e9..d24dcdc16 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INonNegativeIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INonNegativeIntegerItem.java @@ -8,18 +8,27 @@ import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.NonNegativeIntegerItemImpl; +import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.math.BigInteger; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a non-negative integer data value. + */ public interface INonNegativeIntegerItem extends IIntegerItem { - @SuppressWarnings("null") + /** + * The integer value "1". + */ @NonNull - INonNegativeIntegerItem ONE = valueOf(BigInteger.ONE); - @SuppressWarnings("null") + INonNegativeIntegerItem ONE = valueOf(ObjectUtils.notNull(BigInteger.ONE)); + /** + * The integer value "0". + */ @NonNull - INonNegativeIntegerItem ZERO = valueOf(BigInteger.ZERO); + INonNegativeIntegerItem ZERO = valueOf(ObjectUtils.notNull(BigInteger.ZERO)); /** * Create an item from an existing integer value. @@ -33,10 +42,13 @@ public interface INonNegativeIntegerItem extends IIntegerItem { @NonNull static INonNegativeIntegerItem valueOf(@NonNull String value) { try { - return valueOf(new BigInteger(value)); - } catch (NumberFormatException ex) { - throw new InvalidTypeMetapathException(null, - ex.getMessage(), + return valueOf(MetaschemaDataTypeProvider.NON_NEGATIVE_INTEGER.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid non-negative integer value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -51,7 +63,7 @@ static INonNegativeIntegerItem valueOf(@NonNull String value) { * if the provided value is not a non-negative integer */ @NonNull - static INonNegativeIntegerItem valueOf(@NonNull IIntegerItem value) { + static INonNegativeIntegerItem valueOf(@NonNull INumericItem value) { return valueOf(value.asInteger()); } @@ -84,7 +96,7 @@ static INonNegativeIntegerItem valueOf(@NonNull BigInteger value) { if (value.compareTo(BigInteger.ZERO) < 0) { throw new InvalidTypeMetapathException( null, - String.format("Integer value '%s' is negative.", value)); + String.format("Integer value '%s' must not be negative.", value)); } return new NonNegativeIntegerItemImpl(value); } @@ -101,11 +113,19 @@ static INonNegativeIntegerItem valueOf(@NonNull BigInteger value) { */ @NonNull static INonNegativeIntegerItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.NON_NEGATIVE_INTEGER.cast(item); + try { + return item instanceof INonNegativeIntegerItem + ? (INonNegativeIntegerItem) item + : item instanceof INumericItem + ? valueOf((INumericItem) item) + : valueOf(item.asString()); + } catch (InvalidTypeMetapathException ex) { + throw new InvalidValueForCastFunctionException(ex); + } } @Override default INonNegativeIntegerItem castAsType(IAnyAtomicItem item) { - return valueOf(cast(item).asInteger()); + return cast(item); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java index 6bab669ca..2ea3b0f10 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/INumericItem.java @@ -5,7 +5,7 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.ArithmeticFunctionException; import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; @@ -18,6 +18,15 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * Represents an atomic Metapath item containing a numeric data value, which can + * be either an integer or decimal. This interface provides operations for + * numeric type conversion, comparison, and mathematical operations commonly + * used in Metapath expressions. + * + * @see IIntegerItem + * @see IDecimalItem + */ public interface INumericItem extends IAnyAtomicItem { /** @@ -32,7 +41,14 @@ public interface INumericItem extends IAnyAtomicItem { */ @NonNull static INumericItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.DECIMAL.cast(item); + try { + return item instanceof INumericItem + ? (INumericItem) item + : IDecimalItem.valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } /** @@ -110,7 +126,6 @@ default INumericItem round() { * (negative value} or after (positive value) the decimal point. * @return the rounded value */ - @SuppressWarnings("PMD.CognitiveComplexity") // ok @NonNull default INumericItem round(@NonNull IIntegerItem precisionItem) { int precision; @@ -120,44 +135,51 @@ default INumericItem round(@NonNull IIntegerItem precisionItem) { throw new ArithmeticFunctionException(ArithmeticFunctionException.OVERFLOW_UNDERFLOW_ERROR, "Numeric operation overflow/underflow.", ex); } + return precision >= 0 + ? roundWithPositivePrecision(precision) + : roundWithNegativePrecision(precision); + } + + @NonNull + private INumericItem roundWithPositivePrecision(int precision) { + INumericItem retval; + if (this instanceof IIntegerItem) { + retval = this; + } else { + BigDecimal value = asDecimal(); + BigDecimal rounded = value.signum() == -1 + ? value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_DOWN)) + : value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_UP)); + retval = castAsType(IDecimalItem.valueOf(ObjectUtils.notNull(rounded))); + } + return retval; + } + + /** + * Rounds a number to the specified negative precision by: 1. Computing the + * divisor (10^|precision|) 2. If the absolute value is less than the divisor, + * returns 0 3. Otherwise, rounds to the nearest multiple of the divisor + * + * @param precision + * the negative precision to round to + * @return the rounded value + */ + @NonNull + private INumericItem roundWithNegativePrecision(int precision) { + BigInteger value = asInteger(); + BigInteger divisor = BigInteger.TEN.pow(0 - precision); + INumericItem retval; - if (precision >= 0) { - // round to precision decimal places - if (this instanceof IIntegerItem) { - retval = this; - } else { - // IDecimalItem - BigDecimal value = asDecimal(); - if (value.signum() == -1) { - retval = IDecimalItem.valueOf( - ObjectUtils.notNull( - value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_DOWN)))); - } else { - retval = IDecimalItem.valueOf( - ObjectUtils.notNull( - value.round(new MathContext(precision + value.precision() - value.scale(), RoundingMode.HALF_UP)))); - } - - // cast result to original type - retval = castAsType(retval); - } + if (divisor.compareTo(value.abs()) > 0) { + retval = IIntegerItem.ZERO; } else { - // round to a power of 10 - BigInteger value = asInteger(); - BigInteger divisor = BigInteger.TEN.pow(0 - precision); - - @NonNull - BigInteger result; - if (divisor.compareTo(value.abs()) > 0) { - result = ObjectUtils.notNull(BigInteger.ZERO); - } else { - BigInteger remainder = value.mod(divisor); - BigInteger lessRemainder = value.subtract(remainder); - BigInteger halfDivisor = divisor.divide(BigInteger.TWO); - result = ObjectUtils.notNull( - remainder.compareTo(halfDivisor) >= 0 ? lessRemainder.add(divisor) : lessRemainder); - } - retval = IIntegerItem.valueOf(result); + BigInteger remainder = value.mod(divisor); + BigInteger lessRemainder = value.subtract(remainder); + BigInteger halfDivisor = divisor.divide(BigInteger.TWO); + BigInteger roundedValue = remainder.compareTo(halfDivisor) >= 0 + ? lessRemainder.add(divisor) + : lessRemainder; + retval = IIntegerItem.valueOf(ObjectUtils.notNull(roundedValue)); } return retval; } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPositiveIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPositiveIntegerItem.java index 7eff6798c..51014d7f0 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPositiveIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPositiveIntegerItem.java @@ -5,15 +5,21 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; -import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.PositiveIntegerItemImpl; import java.math.BigInteger; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a positive integer data value. + */ public interface IPositiveIntegerItem extends INonNegativeIntegerItem { + /** + * The integer value "1". + */ @SuppressWarnings("null") @NonNull IPositiveIntegerItem ONE = valueOf(BigInteger.ONE); @@ -31,9 +37,12 @@ public interface IPositiveIntegerItem extends INonNegativeIntegerItem { static IPositiveIntegerItem valueOf(@NonNull String value) { try { return valueOf(new BigInteger(value)); - } catch (NumberFormatException ex) { - throw new InvalidTypeMetapathException(null, - ex.getMessage(), + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid positive integer value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -48,7 +57,7 @@ static IPositiveIntegerItem valueOf(@NonNull String value) { * if the provided value is not a positive integer */ @NonNull - static IPositiveIntegerItem valueOf(@NonNull IIntegerItem value) { + static IPositiveIntegerItem valueOf(@NonNull INumericItem value) { return valueOf(value.asInteger()); } @@ -98,11 +107,20 @@ static IPositiveIntegerItem valueOf(@NonNull BigInteger value) { */ @NonNull static IPositiveIntegerItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.POSITIVE_INTEGER.cast(item); + try { + return item instanceof IPositiveIntegerItem + ? (IPositiveIntegerItem) item + : item instanceof INumericItem + ? valueOf((INumericItem) item) + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override default IPositiveIntegerItem castAsType(IAnyAtomicItem item) { - return valueOf(cast(item).asInteger()); + return cast(item); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IStringItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IStringItem.java index b1d7de5c5..3742a9414 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IStringItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IStringItem.java @@ -5,21 +5,39 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; +import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.StringItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a text data value. + */ public interface IStringItem extends IAnyAtomicItem { /** * Construct a new item using the provided string {@code value}. * * @param value - * a string value + * a string value that must conform to Metaschema string validation + * rules * @return the new item + * @throws InvalidTypeMetapathException + * if the value fails string validation */ @NonNull static IStringItem valueOf(@NonNull String value) { - return new StringItemImpl(value); + try { + return new StringItemImpl(MetaschemaDataTypeProvider.STRING.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid string value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } } /** @@ -35,9 +53,12 @@ static IStringItem valueOf(@NonNull String value) { @NonNull static IStringItem cast(@NonNull IAnyAtomicItem item) { try { - return item.asStringItem(); + return item instanceof IStringItem + ? (IStringItem) item + : valueOf(item.asString()); } catch (IllegalStateException ex) { - throw new InvalidValueForCastFunctionException(ex.getLocalizedMessage(), ex); + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java index f95bd0ebc..f60a6e16a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITemporalItem.java @@ -9,6 +9,9 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a temporal data value. + */ public interface ITemporalItem extends IAnyAtomicItem { /** * Determine if the temporal item has a timezone. diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITokenItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITokenItem.java index 9de2438e3..9dacff82e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITokenItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/ITokenItem.java @@ -6,10 +6,15 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.TokenItemImpl; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a text token data value. + */ public interface ITokenItem extends IStringItem { /** * Construct a new item using the provided string {@code value}. @@ -17,13 +22,19 @@ public interface ITokenItem extends IStringItem { * @param value * a string representing a token value * @return the new item + * @throws InvalidTypeMetapathException + * if the provided value is not a token */ @NonNull static ITokenItem valueOf(@NonNull String value) { try { return new TokenItemImpl(MetaschemaDataTypeProvider.TOKEN.parse(value)); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid token value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -40,7 +51,13 @@ static ITokenItem valueOf(@NonNull String value) { */ @NonNull static ITokenItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.TOKEN.cast(item); + try { + return item instanceof ITokenItem + ? (ITokenItem) item + : valueOf(item.asString()); + } catch (InvalidTypeMetapathException ex) { + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUntypedAtomicItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUntypedAtomicItem.java index fc73fec57..af125d870 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUntypedAtomicItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUntypedAtomicItem.java @@ -5,6 +5,9 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; +/** + * An atomic Metapath item containing an untyped atomic data value. + */ public interface IUntypedAtomicItem extends IAnyAtomicItem { // this interface has no additional methods } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUriReferenceItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUriReferenceItem.java index 6a40b728a..ef17d7001 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUriReferenceItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUriReferenceItem.java @@ -6,13 +6,46 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.UriReferenceItemImpl; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a URI reference data value that complies + * with RFC2396. URI references can be absolute URIs, relative URIs, or + * same-document references. + * + * @see java.net.URI + * @see RFC2396 + */ public interface IUriReferenceItem extends IAnyUriItem { + /** + * Construct a new URI item using the provided string {@code value}. + * + * @param value + * a string representing a URI + * @return the new item + * @throws InvalidTypeMetapathException + * if the given string violates RFC2396 + */ + @NonNull + static IUriReferenceItem valueOf(@NonNull String value) { + try { + return valueOf(MetaschemaDataTypeProvider.URI_REFERENCE.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid URI reference value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } + } + /** * Construct a new item using the provided URI {@code value}. * @@ -37,7 +70,16 @@ static IUriReferenceItem valueOf(@NonNull URI value) { */ @NonNull static IUriReferenceItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.URI_REFERENCE.cast(item); + try { + return item instanceof IUriReferenceItem + ? (IUriReferenceItem) item + : item instanceof IAnyUriItem + ? valueOf(((IAnyUriItem) item).asUri()) + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } @Override diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUuidItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUuidItem.java index b942c823c..134a84d69 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUuidItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IUuidItem.java @@ -6,13 +6,40 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.UuidItemImpl; import java.util.UUID; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a UUID data value. + */ public interface IUuidItem extends IStringItem { + /** + * Construct a new UUID item using the provided string {@code value}. + * + * @param value + * a string representing a UUID + * @return the new item + * @throws InvalidTypeMetapathException + * if the given string violates RFC4122 + */ + @NonNull + static IUuidItem valueOf(@NonNull String value) { + try { + return valueOf(MetaschemaDataTypeProvider.UUID.parse(value)); + } catch (IllegalArgumentException ex) { + throw new InvalidTypeMetapathException( + null, + String.format("Invalid UUID value '%s'. %s", + value, + ex.getLocalizedMessage()), + ex); + } + } /** * Construct a new item using the provided {@code value}. @@ -49,7 +76,14 @@ static IUuidItem random() { */ @NonNull static IUuidItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.UUID.cast(item); + try { + return item instanceof IUuidItem + ? (IUuidItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IYearMonthDurationItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IYearMonthDurationItem.java index 20bd2066f..3cffc2934 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IYearMonthDurationItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IYearMonthDurationItem.java @@ -6,13 +6,18 @@ package gov.nist.secauto.metaschema.core.metapath.item.atomic; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.InvalidTypeMetapathException; import gov.nist.secauto.metaschema.core.metapath.function.InvalidValueForCastFunctionException; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.impl.YearMonthDurationItemImpl; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import java.time.Period; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An atomic Metapath item containing a duration data value in years and months. + */ public interface IYearMonthDurationItem extends IDurationItem { /** * Construct a new year month day duration item using the provided string @@ -21,6 +26,9 @@ public interface IYearMonthDurationItem extends IDurationItem { * @param value * a string representing a year month day duration * @return the new item + * @throws InvalidTypeMetapathException + * if the provided string value is not a day/time duration value + * according to ISO 8601 */ @NonNull static IYearMonthDurationItem valueOf(@NonNull String value) { @@ -28,7 +36,11 @@ static IYearMonthDurationItem valueOf(@NonNull String value) { Period period = ObjectUtils.notNull(MetaschemaDataTypeProvider.YEAR_MONTH_DURATION.parse(value).withDays(0)); return valueOf(period); } catch (IllegalArgumentException ex) { - throw new InvalidValueForCastFunctionException(String.format("Unable to parse string value '%s'", value), + throw new InvalidTypeMetapathException( + null, + String.format("Invalid year/month duration value '%s'. %s", + value, + ex.getLocalizedMessage()), ex); } } @@ -55,10 +67,9 @@ static IYearMonthDurationItem valueOf(@NonNull Period value) { * the number of months in the period * @return the new item */ - @SuppressWarnings("null") @NonNull static IYearMonthDurationItem valueOf(int years, int months) { - return valueOf(Period.of(years, months, 0)); + return valueOf(ObjectUtils.notNull(Period.of(years, months, 0))); } /** @@ -73,7 +84,14 @@ static IYearMonthDurationItem valueOf(int years, int months) { */ @NonNull static IYearMonthDurationItem cast(@NonNull IAnyAtomicItem item) { - return MetaschemaDataTypeProvider.YEAR_MONTH_DURATION.cast(item); + try { + return item instanceof IYearMonthDurationItem + ? (IYearMonthDurationItem) item + : valueOf(item.asString()); + } catch (IllegalStateException | InvalidTypeMetapathException ex) { + // asString can throw IllegalStateException exception + throw new InvalidValueForCastFunctionException(ex); + } } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateItem.java similarity index 56% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateItem.java index 35d2a708c..839c1b9c7 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateItem.java @@ -3,10 +3,20 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract implementation of a Metapath atomic item containing a date data + * value. + * + * @param + * the Java type of the wrapped value + */ public abstract class AbstractDateItem extends AbstractTemporalItem implements IDateItem { @@ -25,6 +35,11 @@ public boolean hasTimezone() { return true; } + @Override + public IDateItem castAsType(IAnyAtomicItem item) { + return IDateItem.cast(item); + } + @Override public int hashCode() { return asZonedDateTime().hashCode(); @@ -34,6 +49,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IDateItem && compareTo((IDateItem) obj) == 0); + || obj instanceof IDateItem && compareTo((IDateItem) obj) == 0; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateTimeItem.java similarity index 55% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateTimeItem.java index 0e363d8ab..ea66ba041 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDateTimeItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDateTimeItem.java @@ -3,10 +3,20 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract implementation of a Metapath atomic item containing a date/time + * data value. + * + * @param + * the Java type of the wrapped value + */ public abstract class AbstractDateTimeItem extends AbstractTemporalItem implements IDateTimeItem { @@ -25,6 +35,11 @@ public boolean hasTimezone() { return true; } + @Override + public IDateTimeItem castAsType(IAnyAtomicItem item) { + return IDateTimeItem.cast(item); + } + @Override public int hashCode() { return asZonedDateTime().hashCode(); @@ -34,6 +49,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IDateTimeItem && compareTo((IDateTimeItem) obj) == 0); + || obj instanceof IDateTimeItem && compareTo((IDateTimeItem) obj) == 0; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDecimalItem.java similarity index 66% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDecimalItem.java index 78a5ab290..0d026d9f5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractDecimalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractDecimalItem.java @@ -3,12 +3,21 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract implementation of a Metapath atomic item containing a decimal + * data value. + * + * @param + * the Java type of the wrapped value + */ public abstract class AbstractDecimalItem extends AbstractAnyAtomicItem implements IDecimalItem { @@ -44,8 +53,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof AbstractDecimalItem.MapKey - && getKey().asDecimal().equals(((AbstractDecimalItem.MapKey) obj).getKey().asDecimal())); + obj instanceof AbstractDecimalItem.MapKey + && getKey().asDecimal().equals(((AbstractDecimalItem.MapKey) obj).getKey().asDecimal()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractIntegerItem.java similarity index 79% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractIntegerItem.java index 051ea784f..a408137a4 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractIntegerItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractIntegerItem.java @@ -3,7 +3,9 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; + +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIntegerItem; import java.math.BigDecimal; import java.math.BigInteger; @@ -11,6 +13,10 @@ import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract implementation of a Metapath atomic item containing an integer + * data value. + */ public abstract class AbstractIntegerItem extends AbstractDecimalItem implements IIntegerItem { @@ -55,6 +61,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IIntegerItem && compareTo((IIntegerItem) obj) == 0); + || obj instanceof IIntegerItem && compareTo((IIntegerItem) obj) == 0; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java similarity index 73% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java index 343c829c2..a4e61b21d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractStringItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractStringItem.java @@ -3,21 +3,35 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.util.regex.Pattern; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * A common base class for all items derived from {@link IStringItem}. + */ public abstract class AbstractStringItem extends AbstractAnyAtomicItem implements IStringItem { private static final String WHITESPACE_SEGMENT = "[ \t\r\n]"; + /** + * Pattern to match one or more whitespace characters at the end of a string. + */ private static final Pattern TRIM_END = Pattern.compile(WHITESPACE_SEGMENT + "++$"); + /** + * Pattern to match one or more whitespace characters at the start of a string. + */ private static final Pattern TRIM_START = Pattern.compile("^" + WHITESPACE_SEGMENT + "+"); + /** + * Pattern to match two or more consecutive whitespace characters. + */ private static final Pattern TRIM_MIDDLE = Pattern.compile(WHITESPACE_SEGMENT + "{2,}"); /** @@ -49,7 +63,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IStringItem && compareTo((IStringItem) obj) == 0); + || obj instanceof IStringItem && compareTo((IStringItem) obj) == 0; } private final class MapKey diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractTemporalItem.java similarity index 81% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractTemporalItem.java index 893e10ce2..108d927be 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractTemporalItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractTemporalItem.java @@ -3,14 +3,16 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.ITemporalItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import edu.umd.cs.findbugs.annotations.NonNull; /** - * A base class for temporal items. + * A base class for all items derived from {@link ITemporalItem}. * * @param * the Java type of the wrapped value diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java similarity index 73% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java index d65829b7b..3f8c49d2c 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AbstractUriItem.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AbstractUriItem.java @@ -3,15 +3,21 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.metapath.impl.AbstractStringMapKey; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IAnyUriItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An abstract implementation of a Metapath atomic item containing a URI data + * value. + */ public abstract class AbstractUriItem extends AbstractAnyAtomicItem implements IAnyUriItem { @@ -46,7 +52,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IAnyUriItem && compareTo((IAnyUriItem) obj) == 0); + || obj instanceof IAnyUriItem && compareTo((IAnyUriItem) obj) == 0; } private final class MapKey diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AnyUriItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AnyUriItemImpl.java similarity index 62% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AnyUriItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AnyUriItemImpl.java index aada02c99..7df2600cb 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/AnyUriItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/AnyUriItemImpl.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.UriAdapter; @@ -12,9 +12,18 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class AnyUriItemImpl +/** + * An implementation of a Metapath atomic item containing a URI data value. + */ +public class AnyUriItemImpl extends AbstractUriItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public AnyUriItemImpl(@NonNull URI value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/Base64BinaryItemImpl.java similarity index 66% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/Base64BinaryItemImpl.java index e742a4340..4ec1450b6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/Base64BinaryItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/Base64BinaryItemImpl.java @@ -3,20 +3,32 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.Base64Adapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IBase64BinaryItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.nio.ByteBuffer; import edu.umd.cs.findbugs.annotations.NonNull; -class Base64BinaryItemImpl +/** + * An implementation of a Metapath atomic item containing a Base64 encoded data + * value. + */ +public class Base64BinaryItemImpl extends AbstractAnyAtomicItem implements IBase64BinaryItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public Base64BinaryItemImpl(@NonNull ByteBuffer value) { super(value); } @@ -45,7 +57,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IBase64BinaryItem && compareTo((IBase64BinaryItem) obj) == 0); + || obj instanceof IBase64BinaryItem && compareTo((IBase64BinaryItem) obj) == 0; } private final class MapKey implements IMapKey { @@ -62,8 +74,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof MapKey - && getKey().equals(((MapKey) obj).getKey())); + obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/BooleanItemImpl.java similarity index 66% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/BooleanItemImpl.java index 2c74b9043..09522bc68 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/BooleanItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/BooleanItemImpl.java @@ -3,15 +3,23 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.IDataTypeAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAtomicItemBase; +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.metapath.item.function.IMapKey; import edu.umd.cs.findbugs.annotations.NonNull; -class BooleanItemImpl implements IBooleanItem { +/** + * An implementation of a Metapath atomic item with a boolean value. + */ +public class BooleanItemImpl + extends AbstractAtomicItemBase + implements IBooleanItem { @NonNull private static final String TRUE_STRING = "true"; @NonNull @@ -23,8 +31,14 @@ class BooleanItemImpl implements IBooleanItem { private final boolean booleanValue; - BooleanItemImpl(boolean booleanValue) { - this.booleanValue = booleanValue; + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public BooleanItemImpl(boolean value) { + this.booleanValue = value; } @Override @@ -48,7 +62,7 @@ public IStringItem asStringItem() { } @Override - public IDataTypeAdapter getJavaTypeAdapter() { + public IDataTypeAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.BOOLEAN; } @@ -71,7 +85,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IBooleanItem && compareTo((IBooleanItem) obj) == 0); + || obj instanceof IBooleanItem && compareTo((IBooleanItem) obj) == 0; } private final class MapKey implements IMapKey { @@ -88,8 +102,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof MapKey - && getKey().equals(((MapKey) obj).getKey())); + obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithTimeZoneItemImpl.java similarity index 65% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithTimeZoneItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithTimeZoneItemImpl.java index 1dd0e4efa..af12a8eae 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateTimeWithTimeZoneItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithTimeZoneItemImpl.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.DateTimeWithTZAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; @@ -12,9 +12,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class DateTimeWithTimeZoneItemImpl +/** + * An implementation of a Metapath atomic item containing a date/time data value + * that has a required timezone. + */ +public class DateTimeWithTimeZoneItemImpl extends AbstractDateTimeItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public DateTimeWithTimeZoneItemImpl(@NonNull ZonedDateTime value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithoutTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithoutTimeZoneItemImpl.java new file mode 100644 index 000000000..4885c1e31 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateTimeWithoutTimeZoneItemImpl.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; + +import gov.nist.secauto.metaschema.core.datatype.adapter.DateTimeAdapter; +import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDateTime; + +import java.time.ZonedDateTime; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * An implementation of a Metapath atomic item containing a date/time data value + * that may not have an explicit timezone. + *

+ * For example, when parsing dates from data sources that don't specify timezone + * information, such as "2024-01-01" as compared to "2024-01-01Z" or + * "2024-01-01+05:00". + */ +public class DateTimeWithoutTimeZoneItemImpl + extends AbstractDateTimeItem { + + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public DateTimeWithoutTimeZoneItemImpl(@NonNull AmbiguousDateTime value) { + super(value); + } + + @Override + public ZonedDateTime asZonedDateTime() { + return getValue().getValue(); + } + + @Override + public DateTimeAdapter getJavaTypeAdapter() { + return MetaschemaDataTypeProvider.DATE_TIME; + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithTimeZoneItemImpl.java similarity index 65% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithTimeZoneItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithTimeZoneItemImpl.java index 810aa6066..59de345b8 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DateWithTimeZoneItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithTimeZoneItemImpl.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.DateWithTZAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; @@ -12,9 +12,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class DateWithTimeZoneItemImpl +/** + * An implementation of a Metapath atomic item containing a date data value that + * has a required timezone. + */ +public class DateWithTimeZoneItemImpl extends AbstractDateItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public DateWithTimeZoneItemImpl(@NonNull ZonedDateTime value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithoutTimeZoneItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithoutTimeZoneItemImpl.java new file mode 100644 index 000000000..86f79a4c0 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DateWithoutTimeZoneItemImpl.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; + +import gov.nist.secauto.metaschema.core.datatype.adapter.DateAdapter; +import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDate; + +import java.time.ZonedDateTime; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * An implementation of a Metapath atomic item containing a date data value that + * may not have an explicit timezone. + */ +public class DateWithoutTimeZoneItemImpl + extends AbstractDateItem { + + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public DateWithoutTimeZoneItemImpl(@NonNull AmbiguousDate value) { + super(value); + } + + @Override + public ZonedDateTime asZonedDateTime() { + return getValue().getValue(); + } + + @Override + public DateAdapter getJavaTypeAdapter() { + return MetaschemaDataTypeProvider.DATE; + } + +} diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DayTimeDurationItemImpl.java similarity index 65% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DayTimeDurationItemImpl.java index fdda3e12d..40dfa04af 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DayTimeDurationItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DayTimeDurationItemImpl.java @@ -3,20 +3,32 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.DayTimeAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDayTimeDurationItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.time.Duration; import edu.umd.cs.findbugs.annotations.NonNull; -class DayTimeDurationItemImpl +/** + * An implementation of a Metapath atomic item containing a duration data value + * in days, hours, and seconds. + */ +public class DayTimeDurationItemImpl extends AbstractAnyAtomicItem implements IDayTimeDurationItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public DayTimeDurationItemImpl(@NonNull Duration value) { super(value); } @@ -45,7 +57,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IDayTimeDurationItem && compareTo((IDayTimeDurationItem) obj) == 0); + || obj instanceof IDayTimeDurationItem && compareTo((IDayTimeDurationItem) obj) == 0; } private final class MapKey implements IMapKey { @@ -62,8 +74,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof MapKey - && getKey().equals(((MapKey) obj).getKey())); + obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DecimalItemImpl.java similarity index 57% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DecimalItemImpl.java index 9157b4a58..d7bde671d 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/DecimalItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/DecimalItemImpl.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.DecimalAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDecimalItem; import java.math.BigDecimal; import java.math.BigInteger; @@ -14,8 +15,34 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class DecimalItemImpl +/** + * An implementation of a Metapath atomic item containing a decimal data value. + */ +public class DecimalItemImpl extends AbstractDecimalItem { + @NonNull + private static final BigDecimal BOOLEAN_TRUE = new BigDecimal("1.0"); + @NonNull + private static final BigDecimal BOOLEAN_FALSE = new BigDecimal("0.0"); + + /** + * Get the equivalent decimal value for the provided boolean value. + * + * @param value + * the boolean value + * @return the equivalent decimal value + */ + @NonNull + public static BigDecimal toBigDecimal(boolean value) { + return value ? BOOLEAN_TRUE : BOOLEAN_FALSE; + } + + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public DecimalItemImpl(@NonNull BigDecimal value) { super(value); } @@ -53,6 +80,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IDecimalItem && compareTo((IDecimalItem) obj) == 0); + || obj instanceof IDecimalItem && compareTo((IDecimalItem) obj) == 0; } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/EmailAddressItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/EmailAddressItemImpl.java similarity index 57% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/EmailAddressItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/EmailAddressItemImpl.java index ca284a374..964ffcdfe 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/EmailAddressItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/EmailAddressItemImpl.java @@ -3,17 +3,28 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.EmailAddressAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IEmailAddressItem; import edu.umd.cs.findbugs.annotations.NonNull; -class EmailAddressItemImpl +/** + * An implementation of a Metapath atomic item containing an email address data + * value. + */ +public class EmailAddressItemImpl extends AbstractStringItem implements IEmailAddressItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public EmailAddressItemImpl(@NonNull String value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/HostnameItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/HostnameItemImpl.java similarity index 57% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/HostnameItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/HostnameItemImpl.java index 3176da092..8aa51f65e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/HostnameItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/HostnameItemImpl.java @@ -3,17 +3,27 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.HostnameAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IHostnameItem; import edu.umd.cs.findbugs.annotations.NonNull; -class HostnameItemImpl +/** + * An implementation of a Metapath atomic item containing a hostname data value. + */ +public class HostnameItemImpl extends AbstractStringItem implements IHostnameItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public HostnameItemImpl(@NonNull String value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv4AddressItemImpl.java similarity index 58% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv4AddressItemImpl.java index 0eb50a346..a650364e6 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv4AddressItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv4AddressItemImpl.java @@ -3,18 +3,30 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.IPv4AddressAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv4AddressItem; import edu.umd.cs.findbugs.annotations.NonNull; import inet.ipaddr.ipv4.IPv4Address; -class IPv4AddressItemImpl +/** + * An implementation of a Metapath atomic item containing an IPv4 address data + * value. + */ +public class IPv4AddressItemImpl extends AbstractUntypedAtomicItem implements IIPv4AddressItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public IPv4AddressItemImpl(@NonNull IPv4Address value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv6AddressItemImpl.java similarity index 58% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv6AddressItemImpl.java index 42c03543a..e72eb917e 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IPv6AddressItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IPv6AddressItemImpl.java @@ -3,18 +3,30 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.IPv6AddressAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IIPv6AddressItem; import edu.umd.cs.findbugs.annotations.NonNull; import inet.ipaddr.ipv6.IPv6Address; -class IPv6AddressItemImpl +/** + * An implementation of a Metapath atomic item containing an IPv6 address data + * value. + */ +public class IPv6AddressItemImpl extends AbstractUntypedAtomicItem implements IIPv6AddressItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public IPv6AddressItemImpl(@NonNull IPv6Address value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IntegerItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IntegerItemImpl.java similarity index 56% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IntegerItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IntegerItemImpl.java index 7ff1f5aff..cc4abe588 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/IntegerItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/IntegerItemImpl.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.IntegerAdapter; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; @@ -12,10 +12,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class IntegerItemImpl +/** + * An implementation of a Metapath atomic item containing an integer data value. + */ +public class IntegerItemImpl extends AbstractIntegerItem { - protected IntegerItemImpl(@NonNull BigInteger value) { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public IntegerItemImpl(@NonNull BigInteger value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupLineItemImpl.java similarity index 58% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupLineItemImpl.java index 1ef4ffb98..ef6e1e912 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupLineItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupLineItemImpl.java @@ -3,18 +3,30 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLineAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; import edu.umd.cs.findbugs.annotations.NonNull; -class MarkupLineItemImpl +/** + * An implementation of a Metapath atomic item representing a single line Markup + * data value. + */ +public class MarkupLineItemImpl extends AbstractUntypedAtomicItem implements IMarkupItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public MarkupLineItemImpl(@NonNull MarkupLine value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupMultiLineItemImpl.java similarity index 59% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupMultiLineItemImpl.java index bbd1894ab..167d75ab5 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/MarkupMultiLineItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/MarkupMultiLineItemImpl.java @@ -3,18 +3,30 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultilineAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractUntypedAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IMarkupItem; import edu.umd.cs.findbugs.annotations.NonNull; -class MarkupMultiLineItemImpl +/** + * An implementation of a Metapath atomic item representing a multi-line Markup + * data value. + */ +public class MarkupMultiLineItemImpl extends AbstractUntypedAtomicItem implements IMarkupItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public MarkupMultiLineItemImpl(@NonNull MarkupMultiline value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NcNameItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NcNameItemImpl.java similarity index 59% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NcNameItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NcNameItemImpl.java index 1a98b075c..7b0b9a88b 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NcNameItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NcNameItemImpl.java @@ -3,18 +3,29 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.NcNameAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INcNameItem; import edu.umd.cs.findbugs.annotations.NonNull; +/** + * An implementation of a Metapath atomic item containing a non-colonized name + * data value. + */ @Deprecated(forRemoval = true, since = "0.7.0") -class NcNameItemImpl +public class NcNameItemImpl extends AbstractStringItem implements INcNameItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public NcNameItemImpl(@NonNull String value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NonNegativeIntegerItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NonNegativeIntegerItemImpl.java similarity index 53% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NonNegativeIntegerItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NonNegativeIntegerItemImpl.java index 9d1e97567..cdb07c15f 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/NonNegativeIntegerItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/NonNegativeIntegerItemImpl.java @@ -3,20 +3,31 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.NonNegativeIntegerAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.INonNegativeIntegerItem; import java.math.BigInteger; import edu.umd.cs.findbugs.annotations.NonNull; -class NonNegativeIntegerItemImpl +/** + * An implementation of a Metapath atomic item containing a non-negative integer + * data value. + */ +public class NonNegativeIntegerItemImpl extends AbstractIntegerItem implements INonNegativeIntegerItem { - protected NonNegativeIntegerItemImpl(@NonNull BigInteger value) { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public NonNegativeIntegerItemImpl(@NonNull BigInteger value) { super(value); } @@ -24,4 +35,5 @@ protected NonNegativeIntegerItemImpl(@NonNull BigInteger value) { public NonNegativeIntegerAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.NON_NEGATIVE_INTEGER; } + } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/PositiveIntegerItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/PositiveIntegerItemImpl.java similarity index 53% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/PositiveIntegerItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/PositiveIntegerItemImpl.java index 9f20445c7..95cec453a 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/PositiveIntegerItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/PositiveIntegerItemImpl.java @@ -3,20 +3,31 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.PositiveIntegerAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IPositiveIntegerItem; import java.math.BigInteger; import edu.umd.cs.findbugs.annotations.NonNull; -class PositiveIntegerItemImpl +/** + * An implementation of a Metapath atomic item containing a positive integer + * data value. + */ +public class PositiveIntegerItemImpl extends AbstractIntegerItem implements IPositiveIntegerItem { - protected PositiveIntegerItemImpl(@NonNull BigInteger value) { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ + public PositiveIntegerItemImpl(@NonNull BigInteger value) { super(value); } @@ -24,4 +35,5 @@ protected PositiveIntegerItemImpl(@NonNull BigInteger value) { public PositiveIntegerAdapter getJavaTypeAdapter() { return MetaschemaDataTypeProvider.POSITIVE_INTEGER; } + } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/StringItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/StringItemImpl.java similarity index 61% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/StringItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/StringItemImpl.java index fea0a27c6..2794093df 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/StringItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/StringItemImpl.java @@ -3,16 +3,25 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.StringAdapter; import edu.umd.cs.findbugs.annotations.NonNull; -class StringItemImpl +/** + * An implementation of a Metapath atomic item containing a text data value. + */ +public class StringItemImpl extends AbstractStringItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public StringItemImpl(@NonNull String value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/TokenItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/TokenItemImpl.java similarity index 57% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/TokenItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/TokenItemImpl.java index 185404413..98dd86349 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/TokenItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/TokenItemImpl.java @@ -3,17 +3,28 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.TokenAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.ITokenItem; import edu.umd.cs.findbugs.annotations.NonNull; -class TokenItemImpl +/** + * An implementation of a Metapath atomic item containing a text token data + * value. + */ +public class TokenItemImpl extends AbstractStringItem implements ITokenItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public TokenItemImpl(@NonNull String value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UriReferenceItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UriReferenceItemImpl.java similarity index 58% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UriReferenceItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UriReferenceItemImpl.java index 2e0b6c1a5..e50775222 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UriReferenceItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UriReferenceItemImpl.java @@ -3,19 +3,30 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.UriReferenceAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUriReferenceItem; import java.net.URI; import edu.umd.cs.findbugs.annotations.NonNull; -class UriReferenceItemImpl +/** + * An implementation of a Metapath atomic item containing a URI reference data + * value. + */ +public class UriReferenceItemImpl extends AbstractUriItem implements IUriReferenceItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public UriReferenceItemImpl(@NonNull URI value) { super(value); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java similarity index 68% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java index 69dbdf8b7..94c914961 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/UuidItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/UuidItemImpl.java @@ -3,10 +3,13 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.UuidAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IUuidItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -14,10 +17,19 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class UuidItemImpl +/** + * An implementation of a Metapath atomic item containing a UUID data value. + */ +public class UuidItemImpl extends AbstractAnyAtomicItem implements IUuidItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public UuidItemImpl(@NonNull UUID value) { super(value); } @@ -51,7 +63,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IStringItem && compareTo((IStringItem) obj) == 0); + || obj instanceof IStringItem && compareTo((IStringItem) obj) == 0; } @Override @@ -68,14 +80,14 @@ public IUuidItem getKey() { @Override public int hashCode() { - return getKey().hashCode(); + return getKey().asUuid().hashCode(); } @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof MapKey - && getKey().asUuid().equals(((MapKey) obj).getKey().asUuid())); + obj instanceof MapKey + && getKey().asUuid().equals(((MapKey) obj).getKey().asUuid()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/YearMonthDurationItemImpl.java similarity index 66% rename from core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java rename to core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/YearMonthDurationItemImpl.java index 213acb802..f572a2281 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/YearMonthDurationItemImpl.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/metapath/item/atomic/impl/YearMonthDurationItemImpl.java @@ -3,10 +3,12 @@ * SPDX-License-Identifier: CC0-1.0 */ -package gov.nist.secauto.metaschema.core.metapath.item.atomic; +package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl; import gov.nist.secauto.metaschema.core.datatype.adapter.MetaschemaDataTypeProvider; import gov.nist.secauto.metaschema.core.datatype.adapter.YearMonthAdapter; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.AbstractAnyAtomicItem; +import gov.nist.secauto.metaschema.core.metapath.item.atomic.IYearMonthDurationItem; import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey; import java.time.Period; @@ -14,10 +16,20 @@ import edu.umd.cs.findbugs.annotations.NonNull; -class YearMonthDurationItemImpl +/** + * An implementation of a Metapath atomic item containing a duration data value + * in years and months. + */ +public class YearMonthDurationItemImpl extends AbstractAnyAtomicItem implements IYearMonthDurationItem { + /** + * Construct a new item with the provided {@code value}. + * + * @param value + * the value to wrap + */ public YearMonthDurationItemImpl(@NonNull Period value) { super(value); } @@ -46,7 +58,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj - || (obj instanceof IYearMonthDurationItem && compareTo((IYearMonthDurationItem) obj) == 0); + || obj instanceof IYearMonthDurationItem && compareTo((IYearMonthDurationItem) obj) == 0; } private final class MapKey implements IMapKey { @@ -63,8 +75,8 @@ public int hashCode() { @Override public boolean equals(Object obj) { return this == obj || - (obj instanceof MapKey - && getKey().equals(((MapKey) obj).getKey())); + obj instanceof MapKey + && getKey().equals(((MapKey) obj).getKey()); } } } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java index 7558e2d4b..e94b00a72 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/validation/XmlSchemaContentValidator.java @@ -37,20 +37,29 @@ public class XmlSchemaContentValidator extends AbstractContentValidator { private final Schema schema; - @SuppressWarnings("null") + @SuppressWarnings({ "resource", "PMD.UseTryWithResources" }) @NonNull - private static Schema toSchema(@NonNull List schemaSources) throws SAXException { + private static Schema toSchema(@NonNull List schemaSources) throws IOException { SchemaFactory schemafactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // schemafactory.setResourceResolver(new ClasspathResourceResolver()); - Schema retval; - if (schemaSources.isEmpty()) { - retval = schemafactory.newSchema(); - } else { - retval = schemafactory.newSchema(schemaSources.toArray(new Source[0])); - } - // TODO verify source input streams are closed - return retval; + try { + return ObjectUtils.notNull(schemaSources.isEmpty() + ? schemafactory.newSchema() + : schemafactory.newSchema(schemaSources.toArray(new Source[0]))); + } catch (SAXException ex) { + throw new IOException(ex); + } finally { + // Close all source input streams + for (Source source : schemaSources) { + if (source instanceof StreamSource) { + StreamSource streamSource = (StreamSource) source; + if (streamSource.getInputStream() != null) { + streamSource.getInputStream().close(); + } + } + } + } } /** @@ -58,10 +67,10 @@ private static Schema toSchema(@NonNull List schemaSources) th * * @param schemaSources * the XML schemas to use for validation - * @throws SAXException + * @throws IOException * if an error occurred while parsing the provided XML schemas */ - public XmlSchemaContentValidator(@Owning @NonNull List schemaSources) throws SAXException { + public XmlSchemaContentValidator(@Owning @NonNull List schemaSources) throws IOException { this(toSchema(ObjectUtils.requireNonNull(schemaSources, "schemaSources"))); } diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/ModuleLoader.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/ModuleLoader.java index 8002f23c8..a9e226555 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/ModuleLoader.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/ModuleLoader.java @@ -47,21 +47,25 @@ public class ModuleLoader private final List modulePostProcessors; /** - * Construct a new Metaschema loader. + * Construct a new Metaschema loader, which applies the provided constraints to + * loaded modules. + * + * @param constraints + * a set of Metaschema module constraints to be applied during loading + * @return the loader instance configured with the specified constraints */ - public ModuleLoader() { - this(CollectionUtil.emptyList()); + public static ModuleLoader newInstanceUsingConstraints(@NonNull Collection constraints) { + return new ModuleLoader(CollectionUtil.singletonList(new ExternalConstraintsModulePostProcessor(constraints))); } /** - * Construct a new Metaschema loader, which applies the provided constraints to - * loaded modules. + * Construct a new Metaschema loader with no constraints. * - * @param constraints - * a set of Metaschema module constraints + * @see #newInstanceUsingConstraints(Collection) for creating an instance with + * constraints */ - public ModuleLoader(@NonNull Collection constraints) { - this(CollectionUtil.singletonList(new ExternalConstraintsModulePostProcessor(constraints))); + public ModuleLoader() { + this(CollectionUtil.emptyList()); } /** diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/XmlbeansMarkupWriter.java b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/XmlbeansMarkupWriter.java index 94cf92062..bcbbae206 100644 --- a/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/XmlbeansMarkupWriter.java +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/model/xml/impl/XmlbeansMarkupWriter.java @@ -8,10 +8,10 @@ import com.vladsch.flexmark.parser.ListOptions; import gov.nist.secauto.metaschema.core.datatype.markup.IMarkupString; -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.MarkupVisitor; import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.AbstractMarkupWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.IMarkupVisitor; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.IMarkupWriter; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.MarkupVisitor; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.apache.xmlbeans.XmlCursor; diff --git a/core/src/main/java/gov/nist/secauto/metaschema/core/package-info.java b/core/src/main/java/gov/nist/secauto/metaschema/core/package-info.java new file mode 100644 index 000000000..b3733e1a0 --- /dev/null +++ b/core/src/main/java/gov/nist/secauto/metaschema/core/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: none + * SPDX-License-Identifier: CC0-1.0 + */ + +/** + * Core support for handling Metaschema-based models. + */ + +package gov.nist.secauto.metaschema.core; diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapterTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapterTest.java index 354b989be..aee4a2214 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapterTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateAdapterTest.java @@ -5,17 +5,60 @@ package gov.nist.secauto.metaschema.core.datatype.adapter; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDate; + import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.stream.Stream; import edu.umd.cs.findbugs.annotations.NonNull; class DateAdapterTest { private static final DateAdapter ADAPTER = new DateAdapter(); + private static Stream provideValues() { + return Stream.of( + // Cases without timezone (ambiguous) + Arguments.of("2018-01-01", true, ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of("2020-01-01", true, ZonedDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of("2018-01-01", true, ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of("2000-02-29", true, ZonedDateTime.of(2000, 2, 29, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of("2020-02-29", true, ZonedDateTime.of(2020, 2, 29, 0, 0, 0, 0, ZoneOffset.UTC)), + // Cases with explicit timezone + Arguments.of("2020-06-23Z", false, ZonedDateTime.of(2020, 6, 23, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of("2020-06-23-04:00", false, ZonedDateTime.of(2020, 6, 23, 0, 0, 0, 0, ZoneOffset.of("-04:00")))); + } + + @ParameterizedTest + @MethodSource("provideValues") + void testSimpleDate(@NonNull String actual, boolean ambiguous, @NonNull ZonedDateTime expected) { + AmbiguousDate date = ADAPTER.parse(actual); + assertAll( + () -> assertEquals(ambiguous, !date.hasTimeZone()), + () -> assertEquals(expected, date.getValue())); + } + + private static Stream provideInvalidValues() { + return Stream.of( + // Cases with invalid date values + Arguments.of("2100-02-29"), + Arguments.of("2023-02-30Z"), + Arguments.of("2023-06-31-04:00")); + } + @ParameterizedTest - @ValueSource(strings = { "2018-01-01", "2020-06-23Z", "2020-06-23-04:00", "2020-06-23", "2020-01-01" }) - void testSimpleDate(@NonNull String date) { - ADAPTER.parse(date); + @MethodSource("provideInvalidValues") + void testInvalidDates(@NonNull String actual) { + assertThrows(IllegalArgumentException.class, () -> { + ADAPTER.parse(actual); + }); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapterTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapterTest.java index a98c5a93a..5e083d4bc 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapterTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/adapter/DateTimeAdapterTest.java @@ -5,27 +5,101 @@ package gov.nist.secauto.metaschema.core.datatype.adapter; -import gov.nist.secauto.metaschema.core.util.ObjectUtils; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import gov.nist.secauto.metaschema.core.datatype.object.AmbiguousDateTime; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; class DateTimeAdapterTest { + private static final DateTimeAdapter ADAPTER = new DateTimeAdapter(); - @ParameterizedTest - @ValueSource(strings = { - "2018-01-01T00:00:00", - "2019-09-28T23:20:50.52Z", - "2019-09-28T23:20:50.0Z", - "2019-09-28T23:20:50.5200", - "2019-12-02T16:39:57-08:00", - "2019-12-02T16:39:57.100-08:00", - "2019-12-02T16:39:57", - "2019-12-31T23:59:59Z", - "2019-12-31T23:59:59" - }) - void testSimpleDate(String date) { - new DateTimeAdapter().parse(ObjectUtils.requireNonNull(date)); + /** + * Provides test cases for date-time parsing. + *

+ * Each argument contains: + *

    + *
  • input string to parse
  • + *
  • boolean indicating if the datetime is ambiguous (no timezone)
  • + *
  • expected ZonedDateTime result
  • + *
+ * + * @return Stream of test cases + */ + private static Stream provideValues() { + return Stream.of( + // Cases without timezone (ambiguous) + Arguments.of( + "2018-01-01T00:00:00", + true, + ZonedDateTime.of(2018, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC)), + Arguments.of( + "2019-09-28T23:20:50.5200", + true, + ZonedDateTime.of(2019, 9, 28, 23, 20, 50, toNanos(0.5200), ZoneOffset.UTC)), + Arguments.of( + "2019-12-02T16:39:57", + true, + ZonedDateTime.of(2019, 12, 2, 16, 39, 57, 0, ZoneOffset.UTC)), + Arguments.of( + "2019-12-31T23:59:59", + true, + ZonedDateTime.of(2019, 12, 31, 23, 59, 59, 0, ZoneOffset.UTC)), + // Cases with explicit timezone + Arguments.of( + "2019-09-28T23:20:50.52Z", + false, + ZonedDateTime.of(2019, 9, 28, 23, 20, 50, toNanos(0.52), ZoneOffset.UTC)), + Arguments.of( + "2019-09-28T23:20:50.0Z", + false, + ZonedDateTime.of(2019, 9, 28, 23, 20, 50, 0, ZoneOffset.UTC)), + Arguments.of( + "2019-12-02T16:39:57-08:00", + false, + ZonedDateTime.of(2019, 12, 2, 16, 39, 57, 0, ZoneOffset.of("-08:00"))), + Arguments.of( + "2019-12-02T16:39:57.100-08:00", + false, + ZonedDateTime.of(2019, 12, 2, 16, 39, 57, toNanos(0.100), ZoneOffset.of("-08:00"))), + Arguments.of( + "2019-12-31T23:59:59Z", + false, + ZonedDateTime.of(2019, 12, 31, 23, 59, 59, 0, ZoneOffset.UTC))); } + /** + * Converts a fractional second to nanoseconds. + * + * @param fraction + * the fractional part of a second (0.0 to 0.999...) + * @return the equivalent nanoseconds + * @throws IllegalArgumentException + * if fraction is negative or >= 1 + */ + private static int toNanos(double fraction) { + if (fraction < 0.0 || fraction >= 1.0) { + throw new IllegalArgumentException(String.format("Fraction '%.3f' must be between 0.0 and 0.999...", fraction)); + } + return (int) Math.round(TimeUnit.SECONDS.toNanos(1) * fraction); + } + + @ParameterizedTest + @MethodSource("provideValues") + void testSimpleDateTime(@NonNull String actual, boolean ambiguous, @NonNull ZonedDateTime expected) { + AmbiguousDateTime date = ADAPTER.parse(actual); + assertAll( + () -> assertEquals(ambiguous, !date.hasTimeZone()), + () -> assertEquals(expected, date.getValue())); + } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/CommonmarkConformanceTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/CommonmarkConformanceTest.java index 1de4a8059..a1ce8a251 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/CommonmarkConformanceTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/CommonmarkConformanceTest.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import gov.nist.secauto.metaschema.core.MetaschemaConstants; -import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.XmlMarkupParser; import gov.nist.secauto.metaschema.core.util.ObjectUtils; import org.codehaus.stax2.XMLStreamWriter2; diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupStringTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupStringTest.java index 31dce6abc..93529c84b 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupStringTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/MarkupStringTest.java @@ -17,8 +17,8 @@ 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.InsertAnchorExtension.InsertAnchorNode; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.AstCollectingVisitor; import gov.nist.secauto.metaschema.core.util.CollectionUtil; import org.apache.logging.log4j.LogManager; diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupParserTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupParserTest.java index 41d1bbfe3..e8d998f97 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupParserTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/datatype/markup/flexmark/MarkupParserTest.java @@ -6,10 +6,13 @@ package gov.nist.secauto.metaschema.core.datatype.markup.flexmark; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.ctc.wstx.stax.WstxInputFactory; import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline; +import gov.nist.secauto.metaschema.core.datatype.markup.XmlMarkupParser; +import gov.nist.secauto.metaschema.core.datatype.markup.flexmark.impl.AstCollectingVisitor; import gov.nist.secauto.metaschema.core.model.util.XmlEventUtil; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -39,7 +42,7 @@ void test() throws XMLStreamException { .append("\n") .append("

some text

\n") .append("

text

\n") - .append("

some text .

\n") + .append("

some text .

\n") .append("

Example

\n") .append("

text

\n") .append("
    \n") @@ -48,7 +51,7 @@ void test() throws XMLStreamException { .append("
\n") .append(" \n") .append(" \n") - .append(" \n") + .append(" \n") .append("
Heading 1
data1
data1
\n") .append("

Some more text\"alt\"

\n") .append("
\n") @@ -64,11 +67,18 @@ void test() throws XMLStreamException { assertDoesNotThrow(() -> { MarkupMultiline markupString = XmlMarkupParser.instance().parseMarkupMultiline(reader, resource); - AstCollectingVisitor.asString(markupString.getDocument()); - // System.out.println(html); - // System.out.println(visitor.getAst()); - // System.out.println(markupString.toMarkdown()); - + String ast = AstCollectingVisitor.asString(markupString.getDocument()); + + // Verify specific elements are properly parsed + assertTrue(ast.contains("BulletListItem"), "List items should be present in AST"); + assertTrue(ast.contains("TableBlock"), "Table should be present in AST"); + + // Verify markup conversion + String markdown = markupString.toMarkdown(); + assertTrue(markdown.contains("**list item**"), "Strong text should be converted to markdown"); + assertTrue(markdown.contains("another *list item*"), "Italic text should be converted to markdown"); + assertTrue(markdown.contains("{{ insert: param, param-id }}."), + "Insert placeholder should be converted to markdown"); }); } @@ -95,9 +105,14 @@ void emptyParagraphTest() throws XMLStreamException { assertDoesNotThrow(() -> { MarkupMultiline ms = XmlMarkupParser.instance().parseMarkupMultiline(reader, resource); - LOGGER.atDebug().log("AST: {}", AstCollectingVisitor.asString(ms.getDocument())); - LOGGER.atDebug().log("HTML: {}", ms.toXHtml("")); - LOGGER.atDebug().log("Markdown: {}", ms.toMarkdown()); + String ast = AstCollectingVisitor.asString(ms.getDocument()); + String xhtml = ms.toXHtml(""); + String markdown = ms.toMarkdown(); + + // Verify empty paragraph handling + assertTrue(!ast.contains("Paragraph"), "Empty paragraph should be present in AST"); + assertTrue(xhtml.trim().isEmpty(), "Empty document should produce empty HTML"); + assertTrue(markdown.trim().isEmpty(), "Empty paragraph should produce empty markdown"); }); } } diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeilingTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeilingTest.java index 8da0e03a5..c2ea37004 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeilingTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/metapath/function/library/FnCeilingTest.java @@ -6,6 +6,7 @@ package gov.nist.secauto.metaschema.core.metapath.function.library; import static gov.nist.secauto.metaschema.core.metapath.TestUtils.decimal; +import static gov.nist.secauto.metaschema.core.metapath.TestUtils.integer; import gov.nist.secauto.metaschema.core.metapath.ISequence; import gov.nist.secauto.metaschema.core.metapath.item.atomic.INumericItem; @@ -25,8 +26,11 @@ class FnCeilingTest private static Stream provideValues() { return Stream.of( - Arguments.of(decimal("11"), decimal("10.5")), - Arguments.of(decimal("-10"), decimal("-10.5"))); + Arguments.of(integer(11), decimal("10.5")), + Arguments.of(integer(-10), decimal("-10.5")), + Arguments.of(integer(11), decimal("10.1")), + Arguments.of(integer(0), decimal("0.0")), + Arguments.of(integer(1), decimal("0.999999"))); } @ParameterizedTest diff --git a/core/src/test/java/gov/nist/secauto/metaschema/core/util/UriUtilsTest.java b/core/src/test/java/gov/nist/secauto/metaschema/core/util/UriUtilsTest.java index 224c6cb50..2c85ba7ac 100644 --- a/core/src/test/java/gov/nist/secauto/metaschema/core/util/UriUtilsTest.java +++ b/core/src/test/java/gov/nist/secauto/metaschema/core/util/UriUtilsTest.java @@ -25,7 +25,7 @@ class UriUtilsTest { private static Stream provideValuesTestToUri() throws MalformedURLException, URISyntaxException { - String base = Paths.get("").toAbsolutePath().toUri().toURL().toURI().toASCIIString(); + String base = Paths.get(System.getProperty("user.dir")).toUri().toURL().toURI().toASCIIString(); return Stream.of( Arguments.of("http://example.org/valid", "http://example.org/valid", true), Arguments.of("https://example.org/valid", "https://example.org/valid", true), @@ -46,9 +46,9 @@ private static Stream provideValuesTestToUri() throws MalformedURLExc @MethodSource("provideValuesTestToUri") void testToUri(@NonNull String location, @NonNull String expectedLocation, boolean expectedResult) throws MalformedURLException { - Path cwd = Paths.get(""); + Path cwd = Paths.get(System.getProperty("user.dir")); try { - URI uri = UriUtils.toUri(location, ObjectUtils.notNull(cwd.toAbsolutePath().toUri())).normalize().toURL().toURI(); + URI uri = UriUtils.toUri(location, ObjectUtils.notNull(cwd.toUri())).normalize().toURL().toURI(); System.out.println(String.format("%s -> %s", location, uri.toASCIIString())); assertAll( () -> assertEquals(uri.toASCIIString(), expectedLocation), diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java index a0049ce58..fa7961d2b 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java @@ -10,7 +10,6 @@ import gov.nist.secauto.metaschema.cli.processor.command.AbstractCommandExecutor; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; -import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; import gov.nist.secauto.metaschema.core.util.AutoCloser; import gov.nist.secauto.metaschema.core.util.ObjectUtils; @@ -48,8 +47,8 @@ public abstract class AbstractConvertSubcommand private static final String COMMAND = "convert"; @NonNull private static final List EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( - new DefaultExtraArgument("source-file-or-URL", true), - new DefaultExtraArgument("destination-file", false))); + ExtraArgument.newInstance("source-file-or-URL", true), + ExtraArgument.newInstance("destination-file", false))); @Override public String getName() { diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java index f854b42d6..723e177d1 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java @@ -11,7 +11,6 @@ import gov.nist.secauto.metaschema.cli.processor.command.AbstractCommandExecutor; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; -import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; import gov.nist.secauto.metaschema.cli.util.LoggingValidationHandler; import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration; @@ -58,7 +57,7 @@ public abstract class AbstractValidateContentCommand private static final String COMMAND = "validate"; @NonNull private static final List EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( - new DefaultExtraArgument("file-or-URI-to-validate", true))); + ExtraArgument.newInstance("file-or-URI-to-validate", true))); @NonNull private static final Option CONSTRAINTS_OPTION = ObjectUtils.notNull( diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java index 513265be8..815f49411 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateDiagramCommand.java @@ -9,7 +9,6 @@ import gov.nist.secauto.metaschema.cli.processor.ExitCode; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; -import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor; import gov.nist.secauto.metaschema.core.model.IModule; @@ -53,8 +52,8 @@ class GenerateDiagramCommand static { EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( - new DefaultExtraArgument("metaschema-module-file-or-URL", true), - new DefaultExtraArgument("destination-diagram-file", false))); + ExtraArgument.newInstance("metaschema-module-file-or-URL", true), + ExtraArgument.newInstance("destination-diagram-file", false))); } @Override diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java index ba91e4b69..1ec184da8 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java @@ -9,7 +9,6 @@ import gov.nist.secauto.metaschema.cli.processor.ExitCode; import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand; import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException; -import gov.nist.secauto.metaschema.cli.processor.command.DefaultExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument; import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor; import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration; @@ -36,6 +35,7 @@ import java.util.List; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; /** * This command implementation supports generation of schemas in a variety of @@ -49,8 +49,8 @@ class GenerateSchemaCommand private static final String COMMAND = "generate-schema"; @NonNull private static final List EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of( - new DefaultExtraArgument("metaschema-module-file-or-URL", true), - new DefaultExtraArgument("destination-schema-file", false))); + ExtraArgument.newInstance("metaschema-module-file-or-URL", true), + ExtraArgument.newInstance("destination-schema-file", false))); private static final Option INLINE_TYPES_OPTION = ObjectUtils.notNull( Option.builder() @@ -97,10 +97,6 @@ public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine c * @throws CommandExecutionException * if an error occurred while determining the source format */ - @SuppressWarnings({ - "PMD.OnlyOneReturn", // readability - "PMD.CyclomaticComplexity" - }) protected void executeCommand( @NonNull CallingContext callingContext, @NonNull CommandLine cmdLine) throws CommandExecutionException { @@ -114,6 +110,14 @@ protected void executeCommand( SchemaFormat asFormat = MetaschemaCommands.getSchemaFormat(cmdLine, MetaschemaCommands.AS_SCHEMA_FORMAT_OPTION); + IMutableConfiguration> configuration = createConfiguration(cmdLine, asFormat); + generateSchema(extraArgs, destination, asFormat, configuration); + } + + @NonNull + private static IMutableConfiguration> createConfiguration( + @NonNull CommandLine cmdLine, + @NonNull SchemaFormat asFormat) { IMutableConfiguration> configuration = new DefaultConfiguration<>(); if (cmdLine.hasOption(INLINE_TYPES_OPTION)) { configuration.enableFeature(SchemaGenerationFeature.INLINE_DEFINITIONS); @@ -123,7 +127,14 @@ protected void executeCommand( configuration.enableFeature(SchemaGenerationFeature.INLINE_CHOICE_DEFINITIONS); } } + return configuration; + } + private static void generateSchema( + @NonNull List extraArgs, + @Nullable Path destination, + @NonNull SchemaFormat asFormat, + @NonNull IMutableConfiguration> configuration) throws CommandExecutionException { IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation(); IModule module = MetaschemaCommands.loadModule( ObjectUtils.requireNonNull(extraArgs.get(0)), diff --git a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java index 0d1a0d79c..dc3155147 100644 --- a/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java +++ b/metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/metapath/MetapathCommand.java @@ -19,7 +19,6 @@ public class MetapathCommand * Constructor for a new Metapath command. */ public MetapathCommand() { - super(true); addCommandHandler(new ListFunctionsSubcommand()); addCommandHandler(new EvaluateMetapathCommand()); } diff --git a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java index a8bb95add..c4d70aca3 100644 --- a/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java +++ b/metaschema-cli/src/test/java/gov/nist/secauto/metaschema/cli/CLITest.java @@ -215,8 +215,10 @@ void testValidateContent() { .contains("expect-default-non-zero: Expect constraint '. > 0' did not match the data", "expect-custom-non-zero: No default message, custom error message for expect-custom-non-zero constraint.", "matches-default-regex-letters-only: Value '1' did not match the pattern", - "matches-custom-regex-letters-only: No default message, custom error message for matches-custom-regex-letters-only constraint.", - "cardinality-default-two-minimum: The cardinality '1' is below the required minimum '2' for items matching", + "matches-custom-regex-letters-only: No default message, custom error message for" + + " matches-custom-regex-letters-only constraint.", + "cardinality-default-two-minimum: The cardinality '1' is below the required minimum '2' for items" + + " matching", "index-items-default: Index 'index-items-default' has duplicate key for items", "index-items-custom: No default message, custom error message for index-item-custom.", "is-unique-default: Unique constraint violation at paths", diff --git a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/AbstractTestSuite.java b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/AbstractTestSuite.java index e1c0e2a3f..f1ded262a 100644 --- a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/AbstractTestSuite.java +++ b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/AbstractTestSuite.java @@ -57,6 +57,16 @@ import edu.umd.cs.findbugs.annotations.Nullable; import nl.talsmasoftware.lazy4j.Lazy; +/** + * This abstract implementation dynamically produces JUnit tests based on a test + * suite definition. + * + * @see #getTestSuiteURI() + */ +@SuppressWarnings({ + "PMD.GodClass", + "PMD.CouplingBetweenObjects" +}) public abstract class AbstractTestSuite { private static final Logger LOGGER = LogManager.getLogger(AbstractTestSuite.class); @@ -119,6 +129,9 @@ public abstract class AbstractTestSuite { /** * Dynamically generate the unit tests. * + * @param bindingContext + * the Module binding context + * * @return the steam of unit tests */ @NonNull @@ -223,6 +236,39 @@ private DynamicContainer generateCollection( .sequential()); } + @NonNull + private Path generateSchema( + @NonNull IModule module, + @NonNull Lazy scenarioGenerationPath) { + String schemaExtension; + Format requiredContentFormat = getRequiredContentFormat(); + switch (requiredContentFormat) { + case JSON: + case YAML: + schemaExtension = ".json"; + break; + case XML: + schemaExtension = ".xsd"; + break; + default: + throw new IllegalStateException(); + } + + // determine what file to use for the schema + Path schemaPath; + try { + schemaPath = Files.createTempFile(scenarioGenerationPath.get(), "", "-schema" + schemaExtension); + } catch (IOException ex) { + throw new JUnitException("Unable to create schema temp file", ex); + } + try { + generateSchema(ObjectUtils.notNull(module), ObjectUtils.notNull(schemaPath), getSchemaGeneratorSupplier()); + } catch (IOException ex) { + throw new IllegalStateException(ex); + } + return ObjectUtils.notNull(schemaPath); + } + /** * Generate a schema for the provided module using the provided schema * generator. @@ -270,7 +316,7 @@ private DynamicContainer generateScenario( @NonNull URI collectionUri, @NonNull Lazy collectionGenerationPath, @NonNull IBindingContext bindingContext) { - Lazy scenarioGenerationPath = Lazy.lazy(() -> { + Lazy scenarioGenerationPath = ObjectUtils.notNull(Lazy.lazy(() -> { Path retval; try { retval = Files.createTempDirectory(collectionGenerationPath.get(), "scenario-"); @@ -278,15 +324,7 @@ private DynamicContainer generateScenario( throw new JUnitException("Unable to create scenario temp directory", ex); } return retval; - }); - - // try { - // // create the directories the schema will be stored in - // Files.createDirectories(scenarioGenerationPath); - // } catch (IOException ex) { - // throw new JUnitException("Unable to create test directories for path: " + - // scenarioGenerationPath, ex); - // } + })); GenerateSchema generateSchema = scenario.getGenerateSchema(); MetaschemaDocument.Metaschema metaschemaDirective = generateSchema.getMetaschema(); @@ -301,35 +339,7 @@ private DynamicContainer generateScenario( throw new JUnitException("Unable to generate classes for metaschema: " + metaschemaUri, ex); } - Lazy lazySchema = Lazy.lazy(() -> { - String schemaExtension; - Format requiredContentFormat = getRequiredContentFormat(); - switch (requiredContentFormat) { - case JSON: - case YAML: - schemaExtension = ".json"; - break; - case XML: - schemaExtension = ".xsd"; - break; - default: - throw new IllegalStateException(); - } - - // determine what file to use for the schema - Path schemaPath; - try { - schemaPath = Files.createTempFile(scenarioGenerationPath.get(), "", "-schema" + schemaExtension); - } catch (IOException ex) { - throw new JUnitException("Unable to create schema temp file", ex); - } - try { - generateSchema(ObjectUtils.notNull(module), ObjectUtils.notNull(schemaPath), getSchemaGeneratorSupplier()); - } catch (IOException ex) { - throw new IllegalStateException(ex); - } - return schemaPath; - }); + Lazy lazySchema = Lazy.lazy(() -> generateSchema(module, scenarioGenerationPath)); Lazy lazyContentValidator = Lazy.lazy(() -> { Path schemaPath = lazySchema.get(); diff --git a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/FormatType.java b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/FormatType.java index 709c56f73..10cb4bed0 100644 --- a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/FormatType.java +++ b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/FormatType.java @@ -7,6 +7,9 @@ import gov.nist.secauto.metaschema.databind.io.Format; +/** + * An XMLBeans value handler for parsing and writing {@link Format} values. + */ public final class FormatType { private FormatType() { // disable diff --git a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/GenerationResultType.java b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/GenerationResultType.java index 3a4acc8f9..2c1758b50 100644 --- a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/GenerationResultType.java +++ b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/GenerationResultType.java @@ -5,6 +5,10 @@ package gov.nist.secauto.metaschema.model.testing.xml.xmlbeans.handler; +/** + * An XMLBeans value handler for parsing and writing boolean generation result + * type values. + */ public final class GenerationResultType { private GenerationResultType() { // disable diff --git a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/ValidationResultType.java b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/ValidationResultType.java index d4bf1cce0..02dc90558 100644 --- a/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/ValidationResultType.java +++ b/metaschema-testing/src/main/java/gov/nist/secauto/metaschema/model/testing/xml/xmlbeans/handler/ValidationResultType.java @@ -5,6 +5,11 @@ package gov.nist.secauto.metaschema.model.testing.xml.xmlbeans.handler; +/** + * An XMLBeans value handler for parsing and writing boolean validation result + * type values. + */ + public final class ValidationResultType { private ValidationResultType() { // disable diff --git a/pom.xml b/pom.xml index 0e495dbc1..ac9b6b90b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ dev.metaschema oss-parent - 5 + 6-SNAPSHOT dev.metaschema.java @@ -57,7 +57,6 @@ 2.0.6.1 2.13.1 20240303 - 5.11.3 2.24.1 2.9.3 5.14.2 @@ -65,9 +64,7 @@ ${project.parent.version} 4.0.2 1.2.0 - 7.7.0 12.5 - 4.8.6 4.2.2 3.1.2.RELEASE 7.1.0 @@ -82,13 +79,7 @@ 1.22 2.1.0 9.0.1 - 3.11.1 - 2.12.1 - 3.8.1 - 3.2.0 - 3.26.0 - 4.8.6.5 - 3.5.2 + 3.0.0 -Xmx1024m @@ -437,11 +428,6 @@ compiler 2.26ea0 - - com.github.spotbugs - spotbugs-annotations - ${dependency.spotbugs-annotations.version} - org.eclipse.jdt org.eclipse.jdt.annotation @@ -452,13 +438,6 @@ caffeine ${dependency.caffeine.version} - - org.junit - junit-bom - ${dependency.junit5.version} - pom - import - org.jmock jmock-junit5 @@ -500,11 +479,6 @@ ${dependency.assertj.version} test - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.15.1 - biz.aQute.bnd @@ -549,7 +523,6 @@ org.apache.maven.plugins maven-toolchains-plugin - ${plugin.maven-toolchains.version} @@ -642,7 +615,6 @@ org.apache.maven.plugins maven-jxr-plugin - 3.6.0 **/module-info.java @@ -652,7 +624,6 @@ org.apache.maven.plugins maven-javadoc-plugin - ${plugin.maven-javadoc.version} *.xmlbeans:*.xmlbeans.*:*.antlr false @@ -679,18 +650,12 @@ com.github.spotbugs spotbugs-maven-plugin - ${plugin.spotbugs.version} spotbugs-exclude.xml true spotbugs.sarif - - org.apache.maven.plugins - maven-invoker-plugin - ${plugin.maven-invoker.version} - io.github.git-commit-id git-commit-id-maven-plugin @@ -714,7 +679,6 @@ org.apache.maven.plugins maven-surefire-plugin - ${plugin.maven-surefire.version} 1.5C true @@ -729,19 +693,6 @@ org.apache.maven.plugins maven-pmd-plugin - ${plugin.pmd.version} - - - net.sourceforge.pmd - pmd-core - ${dependency.pmd.version} - - - net.sourceforge.pmd - pmd-java - ${dependency.pmd.version} - - pmd-verify diff --git a/schemagen/src/test/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGeneratorTestSuite.java b/schemagen/src/test/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGeneratorTestSuite.java index 1b082e784..59c6de450 100644 --- a/schemagen/src/test/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGeneratorTestSuite.java +++ b/schemagen/src/test/java/gov/nist/secauto/metaschema/schemagen/AbstractSchemaGeneratorTestSuite.java @@ -26,7 +26,6 @@ import gov.nist.secauto.metaschema.schemagen.xml.XmlSchemaGenerator; import org.junit.platform.commons.JUnitException; -import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; @@ -123,7 +122,7 @@ public abstract class AbstractSchemaGeneratorTestSuite = new StreamSource(schemaResource.openStream(), schemaResource.toString()); List schemaSources = Collections.singletonList(source); return new XmlSchemaContentValidator(schemaSources); - } catch (IOException | SAXException ex) { + } catch (IOException ex) { throw new IllegalStateException(ex); } };