From 7a83460d2885d16a137c6f79ab449f8c6be61b2c Mon Sep 17 00:00:00 2001 From: dgelessus <dgelessus@users.noreply.github.com> Date: Wed, 27 May 2020 16:57:33 +0200 Subject: [PATCH] Track position information when splitting arguments --- .../java/de/prob2/jupyter/CommandUtils.java | 18 ++++---- src/main/java/de/prob2/jupyter/Parameter.java | 45 ++++++++++++------- .../de/prob2/jupyter/PositionedString.java | 33 ++++++++++++++ .../java/de/prob2/jupyter/SplitArguments.java | 8 ++-- .../java/de/prob2/jupyter/SplitResult.java | 6 +-- 5 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 src/main/java/de/prob2/jupyter/PositionedString.java diff --git a/src/main/java/de/prob2/jupyter/CommandUtils.java b/src/main/java/de/prob2/jupyter/CommandUtils.java index 4e66d06..10c9735 100644 --- a/src/main/java/de/prob2/jupyter/CommandUtils.java +++ b/src/main/java/de/prob2/jupyter/CommandUtils.java @@ -54,6 +54,7 @@ public final class CommandUtils { private static final @NotNull Logger LOGGER = LoggerFactory.getLogger(CommandUtils.class); + private static final @NotNull Pattern BODY_SPLIT_PATTERN = Pattern.compile("\\n"); public static final @NotNull Pattern ARG_SPLIT_PATTERN = Pattern.compile("\\s+"); private static final @NotNull Pattern B_IDENTIFIER_PATTERN = Pattern.compile("[A-Za-z][A-Za-z0-9_]*"); @@ -91,18 +92,19 @@ public final class CommandUtils { public static @NotNull SplitResult splitArgs(final @NotNull Parameters parameters, final @NotNull String argString) { final SplitArguments splitArgs = new SplitArguments(Collections.emptyMap()); - String remainingArgs = argString; + PositionedString remainingArgs = new PositionedString(argString, 0); if (parameters.getBodyParam().isPresent()) { - final String[] argsAndBody = argString.split("\n", 2); - if (argsAndBody.length > 1) { - remainingArgs = argsAndBody[0]; - splitArgs.add(parameters.getBodyParam().get(), argsAndBody[1]); + final Matcher bodySplitMatcher = BODY_SPLIT_PATTERN.matcher(argString); + if (bodySplitMatcher.find()) { + remainingArgs = new PositionedString(argString.substring(0, bodySplitMatcher.start()), remainingArgs.getStartPosition()); + final PositionedString bodyValue = new PositionedString(argString.substring(bodySplitMatcher.end()), bodySplitMatcher.end()); + splitArgs.add(parameters.getBodyParam().get(), bodyValue); } } for (int i = 0; i < parameters.getPositionalParameters().size();) { final Parameter<?> param = parameters.getPositionalParameters().get(i); - if (remainingArgs.isEmpty()) { + if (remainingArgs.getValue().isEmpty()) { break; } @@ -123,8 +125,8 @@ public final class CommandUtils { } public static @NotNull ParsedArguments validateSplitArgs(final @NotNull Parameters parameters, final SplitResult split) { - if (!split.getRemaining().isEmpty()) { - throw new UserErrorException("Expected at most " + parameters.getPositionalParameters().size() + " arguments, got extra argument: " + split.getRemaining()); + if (!split.getRemaining().getValue().isEmpty()) { + throw new UserErrorException("Expected at most " + parameters.getPositionalParameters().size() + " arguments, got extra argument: " + split.getRemaining().getValue()); } final ParsedArguments parsed = new ParsedArguments(Collections.emptyMap()); diff --git a/src/main/java/de/prob2/jupyter/Parameter.java b/src/main/java/de/prob2/jupyter/Parameter.java index 06d2825..e13803e 100644 --- a/src/main/java/de/prob2/jupyter/Parameter.java +++ b/src/main/java/de/prob2/jupyter/Parameter.java @@ -2,38 +2,49 @@ package de.prob2.jupyter; import java.util.List; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; public interface Parameter<T> { public static final class SplitResult { - private final @NotNull String splitArg; - private final @NotNull String remainingArgString; + private final @NotNull PositionedString splitArg; + private final @NotNull PositionedString remainingArgString; - public SplitResult(final @NotNull String splitArg, final @NotNull String remainingArgString) { + public SplitResult(final @NotNull PositionedString splitArg, final @NotNull PositionedString remainingArgString) { super(); this.splitArg = splitArg; this.remainingArgString = remainingArgString; } - public @NotNull String getSplitArg() { + public @NotNull PositionedString getSplitArg() { return this.splitArg; } - public @NotNull String getRemainingArgString() { + public @NotNull PositionedString getRemainingArgString() { return this.remainingArgString; } } public interface Splitter { public static final @NotNull Parameter.Splitter REGULAR = argString -> { - final String[] split = CommandUtils.ARG_SPLIT_PATTERN.split(argString, 2); - return new SplitResult(split[0], split.length > 1 ? split[1] : ""); + final Matcher argSplitMatcher = CommandUtils.ARG_SPLIT_PATTERN.matcher(argString.getValue()); + final PositionedString splitArg; + final PositionedString remainingArgString; + if (argSplitMatcher.find()) { + splitArg = new PositionedString(argString.getValue().substring(0, argSplitMatcher.start()), argString.getStartPosition()); + remainingArgString = new PositionedString(argString.getValue().substring(argSplitMatcher.end()), argString.getStartPosition() + argSplitMatcher.end()); + } else { + splitArg = argString; + remainingArgString = new PositionedString("", argString.getStartPosition() + argString.getValue().length()); + } + return new SplitResult(splitArg, remainingArgString); }; - public static final @NotNull Parameter.Splitter REMAINDER = argString -> new SplitResult(argString, ""); + public static final @NotNull Parameter.Splitter REMAINDER = argString -> new SplitResult(argString, new PositionedString("", argString.getStartPosition() + argString.getValue().length())); - public abstract Parameter.SplitResult split(final @NotNull String argString); + public abstract Parameter.SplitResult split(final @NotNull PositionedString argString); } public interface Validator<T> { @@ -44,25 +55,29 @@ public interface Parameter<T> { throw new UserErrorException("Non-repeating parameter " + param.getIdentifier() + " cannot appear more than once"); } - return argValues.get(0); + return argValues.get(0).getValue(); }; 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"); } - return argValues.stream().findAny(); + return argValues.stream().findAny().map(PositionedString::getValue); }; 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()); } - return argValues; + return argValues.stream() + .map(PositionedString::getValue) + .collect(Collectors.toList()); }; - public static final @NotNull Parameter.Validator<@NotNull List<@NotNull String>> ZERO_OR_MORE = (param, argValues) -> argValues; + public static final @NotNull Parameter.Validator<@NotNull List<@NotNull String>> ZERO_OR_MORE = (param, argValues) -> argValues.stream() + .map(PositionedString::getValue) + .collect(Collectors.toList()); - public abstract T validate(final @NotNull Parameter<T> param, final @NotNull List<@NotNull String> argValues); + public abstract T validate(final @NotNull Parameter<T> param, final @NotNull List<@NotNull PositionedString> argValues); } public interface RequiredSingle extends Parameter<@NotNull String> {} @@ -141,7 +156,7 @@ public interface Parameter<T> { throw new AssertionError("Body " + param.getIdentifier() + " appeared more than once, this should never happen!"); } - return argValues.get(0); + return argValues.get(0).getValue(); }; } }; diff --git a/src/main/java/de/prob2/jupyter/PositionedString.java b/src/main/java/de/prob2/jupyter/PositionedString.java new file mode 100644 index 0000000..94c6b0d --- /dev/null +++ b/src/main/java/de/prob2/jupyter/PositionedString.java @@ -0,0 +1,33 @@ +package de.prob2.jupyter; + +import com.google.common.base.MoreObjects; + +import org.jetbrains.annotations.NotNull; + +public final class PositionedString { + private final @NotNull String value; + private final int startPosition; + + public PositionedString(final @NotNull String value, final int startPosition) { + super(); + + this.value = value; + this.startPosition = startPosition; + } + + public @NotNull String getValue() { + return this.value; + } + + public int getStartPosition() { + return this.startPosition; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("value", this.getValue()) + .add("startPosition", this.getStartPosition()) + .toString(); + } +} diff --git a/src/main/java/de/prob2/jupyter/SplitArguments.java b/src/main/java/de/prob2/jupyter/SplitArguments.java index 906b1fb..ed06b9a 100644 --- a/src/main/java/de/prob2/jupyter/SplitArguments.java +++ b/src/main/java/de/prob2/jupyter/SplitArguments.java @@ -11,9 +11,9 @@ import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; public final class SplitArguments { - private final @NotNull Map<@NotNull Parameter<?>, @NotNull List<@NotNull String>> values; + private final @NotNull Map<@NotNull Parameter<?>, @NotNull List<@NotNull PositionedString>> values; - public SplitArguments(final @NotNull Map<@NotNull Parameter<?>, @NotNull List<@NotNull String>> values) { + public SplitArguments(final @NotNull Map<@NotNull Parameter<?>, @NotNull List<@NotNull PositionedString>> values) { super(); this.values = new HashMap<>(values); @@ -23,11 +23,11 @@ public final class SplitArguments { return this.values.containsKey(parameter); } - public @NotNull List<@NotNull String> get(final @NotNull Parameter<?> parameter) { + public @NotNull List<@NotNull PositionedString> get(final @NotNull Parameter<?> parameter) { return this.values.getOrDefault(parameter, Collections.emptyList()); } - public void add(final @NotNull Parameter<?> parameter, final String value) { + 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 deed42c..5ae4694 100644 --- a/src/main/java/de/prob2/jupyter/SplitResult.java +++ b/src/main/java/de/prob2/jupyter/SplitResult.java @@ -4,9 +4,9 @@ import org.jetbrains.annotations.NotNull; public final class SplitResult { private final @NotNull SplitArguments arguments; - private final @NotNull String remaining; + private final @NotNull PositionedString remaining; - public SplitResult(final @NotNull SplitArguments arguments, final @NotNull String remaining) { + public SplitResult(final @NotNull SplitArguments arguments, final @NotNull PositionedString remaining) { super(); this.arguments = arguments; @@ -17,7 +17,7 @@ public final class SplitResult { return this.arguments; } - public @NotNull String getRemaining() { + public @NotNull PositionedString getRemaining() { return this.remaining; } } -- GitLab