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