From 29be62a24a144da06f84fca501a328f40f1e1ab7 Mon Sep 17 00:00:00 2001 From: dgelessus <dgelessus@users.noreply.github.com> Date: Thu, 11 Jun 2020 19:43:01 +0200 Subject: [PATCH] Add internal documentation for the new command parsing code --- .../java/de/prob2/jupyter/CommandUtils.java | 58 ++++++ src/main/java/de/prob2/jupyter/Parameter.java | 193 ++++++++++++++++++ .../java/de/prob2/jupyter/ParameterBase.java | 5 + .../de/prob2/jupyter/ParameterCompleters.java | 19 ++ .../de/prob2/jupyter/ParameterInspectors.java | 19 ++ .../java/de/prob2/jupyter/Parameters.java | 18 ++ .../de/prob2/jupyter/ParsedArguments.java | 26 +++ .../de/prob2/jupyter/PositionedString.java | 46 +++++ .../java/de/prob2/jupyter/SplitArguments.java | 28 +++ .../java/de/prob2/jupyter/SplitResult.java | 25 +++ 10 files changed, 437 insertions(+) diff --git a/src/main/java/de/prob2/jupyter/CommandUtils.java b/src/main/java/de/prob2/jupyter/CommandUtils.java index 1078400..281a116 100644 --- a/src/main/java/de/prob2/jupyter/CommandUtils.java +++ b/src/main/java/de/prob2/jupyter/CommandUtils.java @@ -90,6 +90,26 @@ public final class CommandUtils { } } + /** + * <p> + * Split an argument string according to the given parameter specification. + * The splitting process stops either when the {@code upToPosition} index is exceeded, + * when all parameters have been fully parsed, + * or when the entire argument string has been consumed. + * </p> + * <p> + * This method only splits the argument string and does not perform any validation, + * which means that it will parse any input string without errors + * (although the result might not be meaningful). + * Use {@link #validateSplitArgs(Parameters, SplitResult)} to validate the result of this method, + * or use {@link #parseArgs(Parameters, String)} to perform splitting and validation in a single call. + * </p> + * + * @param parameters the parameter specification based on which the arguments should be split + * @param argString the argument string to split + * @param upToPosition the position in the argument string after which to stop splitting + * @return the result of the split operation (see {@link SplitResult} for details) + */ public static @NotNull SplitResult splitArgs(final @NotNull Parameters parameters, final @NotNull String argString, final int upToPosition) { final SplitArguments splitArgs = new SplitArguments(Collections.emptyMap()); PositionedString remainingArgs = new PositionedString(argString, 0); @@ -134,6 +154,27 @@ public final class CommandUtils { return new SplitResult(splitArgs, parameterAtPosition, remainingArgs); } + /** + * <p> + * Split an argument string according to the given parameter specification. + * The splitting process stops either when all parameters have been fully parsed, + * or when the entire argument string has been consumed. + * </p> + * <p> + * This method only splits the argument string and does not perform any validation, + * which means that it will parse any input string without errors + * (although the result might not be meaningful). + * Use {@link #validateSplitArgs(Parameters, SplitResult)} to validate the result of this method, + * or use {@link #parseArgs(Parameters, String)} to perform splitting and validation in a single call. + * </p> + * <p> + * This method is equivalent to calling {@link #splitArgs(Parameters, String, int)} with {@code upToPosition} set so that as much as possible of the argument string is consumed. + * </p> + * + * @param parameters the parameter specification based on which the arguments should be split + * @param argString the argument string to split + * @return the result of the split operation (see {@link SplitResult} for details) + */ public static @NotNull SplitResult splitArgs(final @NotNull Parameters parameters, final @NotNull String argString) { return splitArgs(parameters, argString, argString.length()); } @@ -142,6 +183,14 @@ public final class CommandUtils { parsed.put(param, param.getValidator().validate(param, splitArgs.get(param))); } + /** + * Validate a {@link SplitResult} according to the given parameter specification. + * + * @param parameters the parameter specification based on which the split result should be validated + * @param split the split result to validate + * @return the parsed arguments after validation + * @throws UserErrorException if the split arguments are invalid + */ public static @NotNull ParsedArguments validateSplitArgs(final @NotNull Parameters parameters, final SplitResult split) { if (!split.getRemaining().getValue().isEmpty()) { throw new UserErrorException("Expected at most " + parameters.getPositionalParameters().size() + " arguments, got extra argument: " + split.getRemaining().getValue()); @@ -158,6 +207,15 @@ public final class CommandUtils { return parsed; } + /** + * Parse an argument string according to the given parameter specification. + * This is a shorthand for calling {@link #splitArgs(Parameters, String)} followed by {@link #validateSplitArgs(Parameters, SplitResult)}. + * + * @param parameters the parameter specification based on which the argument string should be parsed + * @param argString the argument string to parse + * @return the parsed and validated argument string + * @throws UserErrorException if the argument string is invalid + */ public static @NotNull ParsedArguments parseArgs(final @NotNull Parameters parameters, final @NotNull String argString) { return validateSplitArgs(parameters, splitArgs(parameters, argString)); } diff --git a/src/main/java/de/prob2/jupyter/Parameter.java b/src/main/java/de/prob2/jupyter/Parameter.java index 9ef3869..c10e6f7 100644 --- a/src/main/java/de/prob2/jupyter/Parameter.java +++ b/src/main/java/de/prob2/jupyter/Parameter.java @@ -7,7 +7,17 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; +/** + * A parameter of a {@link Command}. + * How a command's arguments are parsed depends on the {@link Parameter} objects returned by its {@link Command#getParameters()} method, + * as well as the {@link Parameter.Splitter} and {@link Parameter.Validator} objects of those parameters. + * + * @param <T> the type of parsed values for this parameter, as returned by its validator + */ public interface Parameter<T> { + /** + * The result of a {@link Parameter.Splitter#split(PositionedString)} call. + */ public static final class SplitResult { private final @NotNull PositionedString splitArg; private final @NotNull PositionedString remainingArgString; @@ -19,16 +29,44 @@ public interface Parameter<T> { this.remainingArgString = remainingArgString; } + /** + * Get the argument value that has been split off by the splitter. + * + * @return the argument value that has been split off by the splitter + */ public @NotNull PositionedString getSplitArg() { return this.splitArg; } + /** + * Get the remainder of the argument string that has not been split yet. + * If the argument string has been fully split, + * this is an empty string positioned at the end of the argument string. + * + * @return the remainder of the argument string that has not been split yet + */ public @NotNull PositionedString getRemainingArgString() { return this.remainingArgString; } } + /** + * <p>Controls how a {@link Parameter} splits its part of the argument string into separate arguments.</p> + * <p> + * Splitters do not perform any kind of validation on the argument strings they receive. + * They must always return some kind of result without failing, + * even when the input string is invalid and would not be accepted by the parameter/command. + * This is necessary because splitters are used to partially parse argument strings that may be incomplete or otherwise invalid, + * for example to implement code completion and inspection. + * Validation of the split arguments is performed separately later, + * by the {@link Parameter.Validator} or the {@link Command#run(ParsedArguments)} method. + * </p> + */ + @FunctionalInterface public interface Splitter { + /** + * A {@link Parameter.Splitter} that splits on whitespace. + */ public static final @NotNull Parameter.Splitter REGULAR = argString -> { final Matcher argSplitMatcher = CommandUtils.ARG_SPLIT_PATTERN.matcher(argString.getValue()); final PositionedString splitArg; @@ -42,12 +80,43 @@ public interface Parameter<T> { } return new SplitResult(splitArg, remainingArgString); }; + + /** + * A {@link Parameter.Splitter} that consumes the entire remaining argument string without splitting. + */ public static final @NotNull Parameter.Splitter REMAINDER = argString -> new SplitResult(argString, argString.substring(argString.getValue().length())); + /** + * <p>Split an argument off the given argument string.</p> + * <p> + * A call to this method must always return some kind of usable result and never fail - + * see the documentation of {@link Parameter.Splitter} for details. + * </p> + * + * @param argString the argument string from which to split a single argument + * @return the split argument and the not yet split remainder of the argument string + */ public abstract Parameter.SplitResult split(final @NotNull PositionedString argString); } + /** + * <p> + * Controls how a {@link Parameter}'s argument values are validated after being split by a {@link Splitter}. + * After the argument values have been validated, + * they may be translated to a different type than the original {@link List}{@code <}{@link String}{@code >}. + * For example, + * single-value parameters are represented as a {@link String} or {@link Optional}{@code <}{@link String}{@code >}, + * depending on whether they are required or optional. + * </p> + * + * @param <T> the type to which the argument values are translated after validation + */ + @FunctionalInterface public interface Validator<T> { + /** + * A {@link Parameter.Validator} that requires exactly one argument value to be present for the parameter. + * The single argument value is returned as a {@link String}. + */ public static final @NotNull Parameter.Validator<@NotNull String> EXACTLY_ONE = (param, argValues) -> { if (argValues.isEmpty()) { throw new UserErrorException("Missing required parameter " + param.getIdentifier()); @@ -57,6 +126,13 @@ public interface Parameter<T> { return argValues.get(0).getValue(); }; + + /** + * A {@link Parameter.Validator} that requires at most one argument value to be present for the parameter. + * The argument value is represented as an {@link Optional}{@code <}{@link String}{@code >}, + * which contains the argument value if it was present, + * or is empty otherwise. + */ public static final @NotNull Parameter.Validator<@NotNull Optional<String>> ZERO_OR_ONE = (param, argValues) -> { if (argValues.size() > 1) { throw new UserErrorException("Non-repeating parameter " + param.getIdentifier() + " cannot appear more than once"); @@ -64,6 +140,12 @@ public interface Parameter<T> { return argValues.stream().findAny().map(PositionedString::getValue); }; + + /** + * A {@link Parameter.Validator} that requires one or more argument value to be present for the parameter. + * The argument values are returned as a {@link List}{@code <}{@link String}{@code >}, + * which is never empty. + */ public static final @NotNull Parameter.Validator<@NotNull List<@NotNull String>> ONE_OR_MORE = (param, argValues) -> { if (argValues.isEmpty()) { throw new UserErrorException("Missing required parameter " + param.getIdentifier()); @@ -73,35 +155,122 @@ public interface Parameter<T> { .map(PositionedString::getValue) .collect(Collectors.toList()); }; + + /** + * A {@link Parameter.Validator} that requires zero or more argument value to be present for the parameter. + * This validator effectively performs no validation. + * The argument values are returned as a {@link List}{@code <}{@link String}{@code >}, + * which may be empty. + */ public static final @NotNull Parameter.Validator<@NotNull List<@NotNull String>> ZERO_OR_MORE = (param, argValues) -> argValues.stream() .map(PositionedString::getValue) .collect(Collectors.toList()); + /** + * Validate the given split argument values and translate them to this validator's parsed value type. + * + * @param param the identifier of the parameter to which this validator belongs (only for use in error and debugging messages) + * @param argValues the split argument values to validate + * @return the argument values in parsed form after validation + * @throws UserErrorException if the split argument values are not valid + */ public abstract T validate(final @NotNull Parameter<T> param, final @NotNull List<@NotNull PositionedString> argValues); } + /** + * A parameter which results in a single value after validation. + */ public interface RequiredSingle extends Parameter<@NotNull String> {} + /** + * A parameter which results in an optional value after validation. + */ public interface OptionalSingle extends Parameter<@NotNull Optional<String>> {} + /** + * A parameter which results in a variable number of values after validation. + */ public interface Multiple extends Parameter<@NotNull List<@NotNull String>> {} + /** + * <p>Return an internal identifier for this parameter.</p> + * <p> + * This identifier should only be used in error messages and for debugging purposes. + * It is <em>not</em> used to programmatically identify the parameter during argument splitting/validation/parsing - + * instead, + * the parameter object itself is used as the identifier, + * for example in the maps in {@link SplitArguments} or {@link ParsedArguments}. + * Because of this, + * parameter identifiers are technically not required to be unique, + * even within a single command, + * although this is strongly recommended. + * </p> + * + * @return an internal identifier for this parameter + */ public abstract @NotNull String getIdentifier(); + /** + * <p> + * Return whether this parameter is repeating. + * If false, + * the argument parser will use this parameter's splitter to split an argument once, + * and then continue with the next parameters to parse the following arguments. + * If true, + * the argument parser will use this parameter's splitter repeatedly to split the remainder of the argument string. + * </p> + * <p> + * This setting has no meaning in some cases, + * for example for body parameters, + * or parameters whose splitter always consumes the entire remaining argument string anyway. + * </p> + * + * @return whether this parameter is repeating + */ public abstract boolean isRepeating(); + /** + * Return this parameter's splitter. + * See the documentation of {@link Parameter.Splitter} for details about the role of splitters in argument parsing. + * + * @return this parameter's splitter + */ public abstract @NotNull Parameter.Splitter getSplitter(); + /** + * Return this parameter's validator. + * See the documentation of {@link Parameter.Validator} for details about the role of validators in argument parsing. + * + * @return this parameter's validator + */ public abstract @NotNull Parameter.Validator<T> getValidator(); + /** + * Create a new parameter that accepts a single value and must always be present. + * + * @param identifier an identifier for the parameter + * @return a required single-value parameter + */ public static Parameter.RequiredSingle required(final String identifier) { return new ParameterBase.RequiredSingle(identifier); } + /** + * Create a new parameter that accepts a single value and might not be present. + * + * @param identifier an identifier for the parameter + * @return an optional single-value parameter + */ public static Parameter.OptionalSingle optional(final String identifier) { return new ParameterBase.OptionalSingle(identifier); } + /** + * Create a new parameter that accepts multiple values and must always have at least one value present. + * + * @param identifier an identifier for the parameter + * @return a required multi-value parameter + */ public static Parameter.Multiple requiredMultiple(final String identifier) { return new ParameterBase.Multiple(identifier) { @Override @@ -111,6 +280,12 @@ public interface Parameter<T> { }; } + /** + * Create a new parameter that accepts multiple values and might not have any value present. + * + * @param identifier an identifier for the parameter + * @return an optional multi-value parameter + */ public static Parameter.Multiple optionalMultiple(final String identifier) { return new ParameterBase.Multiple(identifier) { @Override @@ -120,6 +295,12 @@ public interface Parameter<T> { }; } + /** + * Create a new parameter that consumes the entire remaining argument string as a single value and must always be present. + * + * @param identifier an identifier for the parameter + * @return a required remainder parameter + */ public static Parameter.RequiredSingle requiredRemainder(final String identifier) { return new ParameterBase.RequiredSingle(identifier) { @Override @@ -129,6 +310,12 @@ public interface Parameter<T> { }; } + /** + * Create a new parameter that consumes the entire remaining argument string as a single value and might not be present. + * + * @param identifier an identifier for the parameter + * @return an optional remainder parameter + */ public static Parameter.OptionalSingle optionalRemainder(final String identifier) { return new ParameterBase.OptionalSingle(identifier) { @Override @@ -138,6 +325,12 @@ public interface Parameter<T> { }; } + /** + * Create a body parameter that must always be present. + * + * @param identifier an identifier for the parameter + * @return a required body parameter + */ public static Parameter.RequiredSingle body(final String identifier) { return new ParameterBase.RequiredSingle(identifier) { @Override diff --git a/src/main/java/de/prob2/jupyter/ParameterBase.java b/src/main/java/de/prob2/jupyter/ParameterBase.java index 85228fd..d342994 100644 --- a/src/main/java/de/prob2/jupyter/ParameterBase.java +++ b/src/main/java/de/prob2/jupyter/ParameterBase.java @@ -7,6 +7,11 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * Internal base classes for the {@link Parameter} implementations returned by {@link Parameter}'s static factory methods. + * + * @param <T> the type of parsed values for this parameter, as returned by its validator + */ abstract class ParameterBase<T> implements Parameter<T> { static class RequiredSingle extends ParameterBase<@NotNull String> implements Parameter.RequiredSingle { protected RequiredSingle(final @NotNull String identifier) { diff --git a/src/main/java/de/prob2/jupyter/ParameterCompleters.java b/src/main/java/de/prob2/jupyter/ParameterCompleters.java index 98e98bd..20446bb 100644 --- a/src/main/java/de/prob2/jupyter/ParameterCompleters.java +++ b/src/main/java/de/prob2/jupyter/ParameterCompleters.java @@ -9,7 +9,14 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * An unmodifiable collection of per-parameter code completion handlers for a command. + */ public final class ParameterCompleters { + /** + * An empty set of code completion handlers, + * for commands that do not accept any arguments or do not implement code completion. + */ public static final @NotNull ParameterCompleters NONE = new ParameterCompleters(Collections.emptyMap()); private final @NotNull Map<@NotNull Parameter<?>, CommandUtils.@NotNull Completer> completers; @@ -20,6 +27,11 @@ public final class ParameterCompleters { this.completers = Collections.unmodifiableMap(new HashMap<>(completers)); } + /** + * Return the stored mapping of parameters to code completion handlers. + * + * @return the stored mapping of parameters to code completion handlers + */ public @NotNull Map<@NotNull Parameter<?>, CommandUtils.@NotNull Completer> getCompleters() { return this.completers; } @@ -31,6 +43,13 @@ public final class ParameterCompleters { .toString(); } + /** + * Return the code completion handler for a parameter, if any. + * + * @param parameter the parameter for which to look up the code completion handler + * @return the code completion handler for a parameter, + * or {@link Optional#empty()} if there is none + */ public @NotNull Optional<CommandUtils.Completer> getCompleterForParameter(final @NotNull Parameter<?> parameter) { if (this.getCompleters().containsKey(parameter)) { return Optional.of(this.getCompleters().get(parameter)); diff --git a/src/main/java/de/prob2/jupyter/ParameterInspectors.java b/src/main/java/de/prob2/jupyter/ParameterInspectors.java index e848396..f0fd3bc 100644 --- a/src/main/java/de/prob2/jupyter/ParameterInspectors.java +++ b/src/main/java/de/prob2/jupyter/ParameterInspectors.java @@ -9,7 +9,14 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * An unmodifiable collection of per-parameter inspection handlers for a command. + */ public final class ParameterInspectors { + /** + * An empty set of inspection handlers, + * for commands that do not accept any arguments or do not implement inspection. + */ public static final @NotNull ParameterInspectors NONE = new ParameterInspectors(Collections.emptyMap()); private final @NotNull Map<@NotNull Parameter<?>, CommandUtils.@NotNull Inspector> inspectors; @@ -20,6 +27,11 @@ public final class ParameterInspectors { this.inspectors = Collections.unmodifiableMap(new HashMap<>(inspectors)); } + /** + * Return the stored mapping of parameters to inspection handlers. + * + * @return the stored mapping of parameters to inspection handlers + */ public @NotNull Map<@NotNull Parameter<?>, CommandUtils.@NotNull Inspector> getInspectors() { return this.inspectors; } @@ -31,6 +43,13 @@ public final class ParameterInspectors { .toString(); } + /** + * Return the inspection handler for a parameter, if any. + * + * @param parameter the parameter for which to look up the inspection handler + * @return the inspection handler for a parameter, + * or {@link Optional#empty()} if there is none + */ public @NotNull Optional<CommandUtils.Inspector> getInspectorForParameter(final @NotNull Parameter<?> parameter) { if (this.getInspectors().containsKey(parameter)) { return Optional.of(this.getInspectors().get(parameter)); diff --git a/src/main/java/de/prob2/jupyter/Parameters.java b/src/main/java/de/prob2/jupyter/Parameters.java index 4d08aa6..3047619 100644 --- a/src/main/java/de/prob2/jupyter/Parameters.java +++ b/src/main/java/de/prob2/jupyter/Parameters.java @@ -7,7 +7,15 @@ import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * A specification for the parameters accepted by a {@link Command}. + * This information is used by {@link ProBKernel} to parse the arguments of a command call for exection, inspection, and code completion. + */ public final class Parameters { + /** + * A parameter specification that has no parameters, + * for commands that do not accept any arguments. + */ public static final @NotNull Parameters NONE = new Parameters(Collections.emptyList()); private final @NotNull List<Parameter<?>> positionalParameters; @@ -40,10 +48,20 @@ public final class Parameters { this(positionalParameters, null); } + /** + * Return the list of accepted positional parameters. + * + * @return the list of accepted positional parameters + */ public @NotNull List<Parameter<?>> getPositionalParameters() { return this.positionalParameters; } + /** + * Return the positional parameter for the body, if any. + * + * @return the positional parameter for the body, if any + */ public @NotNull Optional<Parameter.RequiredSingle> getBodyParam() { return Optional.ofNullable(this.bodyParam); } diff --git a/src/main/java/de/prob2/jupyter/ParsedArguments.java b/src/main/java/de/prob2/jupyter/ParsedArguments.java index 3d8a81e..8485d2c 100644 --- a/src/main/java/de/prob2/jupyter/ParsedArguments.java +++ b/src/main/java/de/prob2/jupyter/ParsedArguments.java @@ -7,6 +7,12 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * A collection of parsed and validated command arguments, + * mapped to their parameters. + * Internally this is a map of parameters to arbitrary objects, + * but the methods of this class ensure that parameters can only be mapped to their declared parsed value type. + */ public final class ParsedArguments { private final @NotNull Map<@NotNull Parameter<?>, Object> values; @@ -16,10 +22,23 @@ public final class ParsedArguments { this.values = new HashMap<>(values); } + /** + * Return whether a value has been associated with the given parameter. + * + * @param parameter the parameter for which to check for a value + * @return whether a value has been associated with the given parameter + */ public boolean containsKey(final @NotNull Parameter<?> parameter) { return this.values.containsKey(parameter); } + /** + * Return the parsed value associated with the given parameter. + * + * @param parameter the parameter for which to return the value + * @param <T> the type of parsed value for the parameter + * @return the parsed value associated with the given parameter + */ public <T> T get(final @NotNull Parameter<T> parameter) { if (!this.containsKey(parameter)) { throw new IllegalArgumentException("No value present for parameter " + parameter); @@ -29,6 +48,13 @@ public final class ParsedArguments { return value; } + /** + * Associate a parsed value with the given parameter. + * + * @param parameter the parameter with which to associate the value + * @param value the parsed value to associate with the parameter + * @param <T> the type of parsed value for the parameter + */ public <T> void put(final @NotNull Parameter<T> parameter, final T value) { this.values.put(parameter, value); } diff --git a/src/main/java/de/prob2/jupyter/PositionedString.java b/src/main/java/de/prob2/jupyter/PositionedString.java index 494748c..ba32db9 100644 --- a/src/main/java/de/prob2/jupyter/PositionedString.java +++ b/src/main/java/de/prob2/jupyter/PositionedString.java @@ -4,6 +4,10 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * A string with a known position relative to a known origin or larger string. + * This class is used while splitting/parsing commands and arguments to track the position of individual argument strings within the source code. + */ public final class PositionedString { private final @NotNull String value; private final int startPosition; @@ -15,22 +19,64 @@ public final class PositionedString { this.startPosition = startPosition; } + /** + * Return this string's text value. + * + * @return this string's text value + */ public @NotNull String getValue() { return this.value; } + /** + * Return the position of the start of this string. + * + * @return the position of the start of this string relative to a known origin + */ public int getStartPosition() { return this.startPosition; } + /** + * <p> + * Return a substring of this string, with the position adjusted accordingly. + * This method behaves like {@link String#substring(int, int)}. + * </p> + * <p> + * {@code beginIndex} and {@code endIndex} are relative to the underlying text value and are not affected by this string's position information. + * </p> + * + * @param beginIndex the beginning index of the substring, inclusive + * @param endIndex the end index of the substring, exclusive + * @return the substring of this string, from {@code beginIndex} to {@code endIndex}, with the position increased by {@code beginIndex} + */ public PositionedString substring(final int beginIndex, final int endIndex) { return new PositionedString(this.getValue().substring(beginIndex, endIndex), this.getStartPosition() + beginIndex); } + /** + * <p> + * Return a substring of this string, with the position adjusted accordingly. + * This method behaves like {@link String#substring(int)}. + * </p> + * <p> + * {@code beginIndex} is relative to the underlying text value and is not affected by this string's position information. + * </p> + * + * @param beginIndex the beginning index of the substring, inclusive + * @return the substring of this string, from {@code beginIndex} to the end of the string, with the position increased by {@code beginIndex} + */ public PositionedString substring(final int beginIndex) { return this.substring(beginIndex, this.getValue().length()); } + /** + * Return a text representation of this string for debugging purposes. + * This method does <em>not</em> return the string's underyling text value - + * {@link #getValue()} should be used for that purpose. + * + * @return a text representation of this string for debugging + */ @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/src/main/java/de/prob2/jupyter/SplitArguments.java b/src/main/java/de/prob2/jupyter/SplitArguments.java index ed06b9a..6a9d312 100644 --- a/src/main/java/de/prob2/jupyter/SplitArguments.java +++ b/src/main/java/de/prob2/jupyter/SplitArguments.java @@ -10,6 +10,12 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; +/** + * A collection of command arguments that have been split and mapped to their parameters, + * but not fully validated yet. + * Internally this is a simple map of parameter objects to lists of strings, + * but this class provides a more convenient interface for the specialized purpose of this map. + */ public final class SplitArguments { private final @NotNull Map<@NotNull Parameter<?>, @NotNull List<@NotNull PositionedString>> values; @@ -19,14 +25,36 @@ public final class SplitArguments { this.values = new HashMap<>(values); } + /** + * Return whether any values have been associated with the given parameter. + * + * @param parameter the parameter for which to check for values + * @return whether any values have been associated with the given parameter + */ public boolean containsKey(final @NotNull Parameter<?> parameter) { return this.values.containsKey(parameter); } + /** + * Return the list of values associated with the given parameter. + * If there are no associated values + * (i. e. {@link #containsKey(Parameter)} returns {@code false} for the parameter), + * no exception is thrown and an empty list is returned. + * + * @param parameter the parameter for which to get values + * @return the list of values associated with the given parameter, + * or an empty list if there are none + */ public @NotNull List<@NotNull PositionedString> get(final @NotNull Parameter<?> parameter) { return this.values.getOrDefault(parameter, Collections.emptyList()); } + /** + * Add a new value to the list of values associated with the given parameter. + * + * @param parameter the parameter with which to associate the value + * @param value the value to add + */ public void add(final @NotNull Parameter<?> parameter, final PositionedString value) { this.values.computeIfAbsent(parameter, p -> new ArrayList<>()).add(value); } diff --git a/src/main/java/de/prob2/jupyter/SplitResult.java b/src/main/java/de/prob2/jupyter/SplitResult.java index 1cf5768..ec067a8 100644 --- a/src/main/java/de/prob2/jupyter/SplitResult.java +++ b/src/main/java/de/prob2/jupyter/SplitResult.java @@ -7,6 +7,9 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * The unvalidated result of a command argument splitting operation ({@link CommandUtils#splitArgs(Parameters, String, int)} or {@link CommandUtils#splitArgs(Parameters, String)}). + */ public final class SplitResult { private final @NotNull SplitArguments arguments; private final @Nullable Parameter<?> parameterAtPosition; @@ -20,14 +23,36 @@ public final class SplitResult { this.remaining = remaining; } + /** + * Return the split arguments from the input string, + * mapped to their respective parameters. + * + * @return the split arguments from the input string, + * mapped to their respective parameters + */ public @NotNull SplitArguments getArguments() { return this.arguments; } + /** + * Return the parameter to which the last split argument belongs, + * or {@link Optional#empty()} if no arguments have been split. + * This information is mainly useful when calling {@link CommandUtils#splitArgs(Parameters, String, int)} with an explicit {@code upToPosition} argument. + * + * @return the parameter to which the last split argument belongs, + * or {@link Optional#empty()} if no arguments have been split + */ public @NotNull Optional<Parameter<?>> getParameterAtPosition() { return Optional.ofNullable(this.parameterAtPosition); } + /** + * Return the remainder of the argument string that has not been split. + * If the argument string was consumed completely by the split operation, + * this is an empty string. + * + * @return the remainder of the argument string that has not been split + */ public @NotNull PositionedString getRemaining() { return this.remaining; } -- GitLab