Compare commits
15 commits
single_quo
...
master
Author | SHA1 | Date | |
---|---|---|---|
6c71b666af | |||
|
f20bede62a | ||
|
a3f3eb2929 | ||
|
514387ba11 | ||
|
a6c4baa1b6 | ||
|
b0f5818a59 | ||
|
b411777f47 | ||
|
0c9c87e4e6 | ||
|
3c773a7188 | ||
|
06df81922f | ||
|
cf754c4ef6 | ||
|
60a94e529d | ||
|
242de3fe73 | ||
|
55f6e25c03 | ||
|
559d8f3972 |
20 changed files with 354 additions and 72 deletions
51
.ado/build.yml
Normal file
51
.ado/build.yml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
name: $(Rev:r)
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- '*'
|
||||||
|
exclude:
|
||||||
|
- master
|
||||||
|
|
||||||
|
pr:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: 'Build'
|
||||||
|
displayName: 'Build for testing'
|
||||||
|
|
||||||
|
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||||
|
container: adoptopenjdk/openjdk8:latest
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: Gradle@2
|
||||||
|
displayName: Build and Test
|
||||||
|
inputs:
|
||||||
|
workingDirectory: ''
|
||||||
|
gradleWrapperFile: 'gradlew'
|
||||||
|
gradleOptions: '-Xmx3072m'
|
||||||
|
options: '-PbuildNumber=0'
|
||||||
|
javaHomeOption: 'JDKVersion'
|
||||||
|
jdkUserInputPath: '/usr/java/openjdk-8'
|
||||||
|
testResultsFiles: '**/TEST-*.xml'
|
||||||
|
tasks: 'build test publish'
|
||||||
|
|
||||||
|
# This is a workaround for ComponentGovernanceComponentDetection@0 not recognizing the generated `.pom` file(s)
|
||||||
|
- task: Bash@3
|
||||||
|
displayName: Copy pom for component governance
|
||||||
|
inputs:
|
||||||
|
targetType: 'inline'
|
||||||
|
script: |
|
||||||
|
pompath=`find build/repo -name *.pom`
|
||||||
|
cp "${pompath}" build/pom.xml
|
||||||
|
|
||||||
|
- task: ComponentGovernanceComponentDetection@0
|
||||||
|
inputs:
|
||||||
|
scanType: 'Register'
|
||||||
|
verbosity: 'Verbose'
|
||||||
|
alertWarningLevel: 'High'
|
87
.ado/release.yml
Normal file
87
.ado/release.yml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
name: $(Rev:r)
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- master
|
||||||
|
|
||||||
|
pr: none
|
||||||
|
|
||||||
|
variables:
|
||||||
|
rConnection: 'mc-java-sc'
|
||||||
|
storageAccount: 'librariesminecraftnet'
|
||||||
|
storageAccountContainer: 'librariesminecraftnet'
|
||||||
|
keyVault: 'mc-java-vault'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: 'Build'
|
||||||
|
displayName: 'Build for release'
|
||||||
|
|
||||||
|
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||||
|
container: adoptopenjdk/openjdk8:latest
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: Gradle@2
|
||||||
|
displayName: Build and Test
|
||||||
|
inputs:
|
||||||
|
workingDirectory: ''
|
||||||
|
gradleWrapperFile: 'gradlew'
|
||||||
|
gradleOptions: '-Xmx3072m'
|
||||||
|
options: '-PbuildNumber=$(Build.BuildNumber)'
|
||||||
|
javaHomeOption: 'JDKVersion'
|
||||||
|
jdkUserInputPath: '/usr/java/openjdk-8'
|
||||||
|
testResultsFiles: '**/TEST-*.xml'
|
||||||
|
tasks: 'build test publish report'
|
||||||
|
|
||||||
|
# This is a workaround for ComponentGovernanceComponentDetection@0 not recognizing the generated `.pom` file(s)
|
||||||
|
- task: Bash@3
|
||||||
|
displayName: Copy pom for component governance
|
||||||
|
inputs:
|
||||||
|
targetType: 'inline'
|
||||||
|
script: |
|
||||||
|
pompath=`find build/repo -name *.pom`
|
||||||
|
cp "${pompath}" build/pom.xml
|
||||||
|
|
||||||
|
- task: ComponentGovernanceComponentDetection@0
|
||||||
|
inputs:
|
||||||
|
scanType: 'Register'
|
||||||
|
verbosity: 'Verbose'
|
||||||
|
alertWarningLevel: 'High'
|
||||||
|
|
||||||
|
- publish: 'build/repo/'
|
||||||
|
artifact: repo
|
||||||
|
|
||||||
|
- job: 'Publish'
|
||||||
|
displayName: 'Publish release'
|
||||||
|
dependsOn: Build
|
||||||
|
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||||
|
|
||||||
|
pool: 'MC-Build-1ES-Azure-Pipeline-Linux'
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
clean: all
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- download: current
|
||||||
|
artifact: repo
|
||||||
|
|
||||||
|
- task: AzureKeyVault@1
|
||||||
|
displayName: 'Fetching secrets'
|
||||||
|
name: secrets
|
||||||
|
inputs:
|
||||||
|
azureSubscription: '$(rConnection)'
|
||||||
|
KeyVaultName: '$(keyVault)'
|
||||||
|
SecretsFilter: 'access-key-prod-librariesminecraftnet'
|
||||||
|
RunAsPreJob: false
|
||||||
|
|
||||||
|
- task: AzureCLI@2
|
||||||
|
displayName: Azure CLI
|
||||||
|
inputs:
|
||||||
|
azureSubscription: '$(rConnection)'
|
||||||
|
scriptType: 'bash'
|
||||||
|
scriptLocation: 'inlineScript'
|
||||||
|
inlineScript: |
|
||||||
|
az storage blob upload-batch -s '$(Pipeline.Workspace)/repo' -d $(storageAccountContainer) --account-name $(storageAccount) --account-key $(access-key-prod-librariesminecraftnet)
|
22
.github/workflows/pr-check.yml
vendored
Normal file
22
.github/workflows/pr-check.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
name: pr-check
|
||||||
|
|
||||||
|
on: [ pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build and test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: adoptopenjdk/openjdk8:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build with Gradle
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
arguments: build test
|
||||||
|
- name: Publish Test Report
|
||||||
|
uses: mikepenz/action-junit-report@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
17
.travis.yml
17
.travis.yml
|
@ -1,17 +0,0 @@
|
||||||
sudo: false
|
|
||||||
dist: trusty
|
|
||||||
language: java
|
|
||||||
|
|
||||||
before_cache:
|
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
|
||||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.gradle/caches/
|
|
||||||
- $HOME/.gradle/wrapper/
|
|
||||||
|
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./gradlew check --info -S --parallel
|
|
|
@ -128,8 +128,8 @@ the reason why is inside this exception map.
|
||||||
## Displaying usage info
|
## Displaying usage info
|
||||||
There are two forms of "usage strings" provided by this library, both require a target node.
|
There are two forms of "usage strings" provided by this library, both require a target node.
|
||||||
|
|
||||||
`getAllUsage(node, source, restricted)` will return a list of all possible commands (executable end-points) under the target node and their human readable path. If `restricted`, it will ignore commands that `source` does not have access to. This will look like [`foo`, `foo <bar>`]
|
`getAllUsage(node, source, restricted)` will return a list of all possible commands (executable end-points) under the target node and their human readable path. If `restricted`, it will ignore commands that `source` does not have access to. This will look like [`foo`, `foo <bar>`].
|
||||||
|
|
||||||
`getSmartUsage(node, source)` will return a map of the child nodes to their "smart usage" human readable path. This tries to squash future-nodes together and show optional & typed information, and can look like `foo (<bar>)`
|
`getSmartUsage(node, source)` will return a map of the child nodes to their "smart usage" human readable path. This tries to squash future-nodes together and show optional & typed information, and can look like `foo (<bar>)`.
|
||||||
|
|
||||||
[![GitHub forks](https://img.shields.io/github/forks/Mojang/brigadier.svg?style=social&label=Fork)](https://github.com/Mojang/brigadier/fork) [![GitHub stars](https://img.shields.io/github/stars/Mojang/brigadier.svg?style=social&label=Stars)](https://github.com/Mojang/brigadier/stargazers)
|
[![GitHub forks](https://img.shields.io/github/forks/Mojang/brigadier.svg?style=social&label=Fork)](https://github.com/Mojang/brigadier/fork) [![GitHub stars](https://img.shields.io/github/stars/Mojang/brigadier.svg?style=social&label=Stars)](https://github.com/Mojang/brigadier/stargazers)
|
||||||
|
|
27
build.gradle
27
build.gradle
|
@ -1,6 +1,4 @@
|
||||||
import groovy.io.FileType
|
import groovy.io.FileType
|
||||||
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider
|
|
||||||
import com.amazonaws.services.s3.AmazonS3Client
|
|
||||||
|
|
||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
@ -15,10 +13,6 @@ buildscript {
|
||||||
url "https://libraries.minecraft.net"
|
url "https://libraries.minecraft.net"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.amazonaws:aws-java-sdk:1.11.33'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -86,18 +80,12 @@ publishing {
|
||||||
|
|
||||||
task report {
|
task report {
|
||||||
doLast {
|
doLast {
|
||||||
println "##teamcity[buildNumber '${project.version}']"
|
println "##vso[build.updatebuildnumber]${project.version}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def publishDir = file("$buildDir/repo")
|
def publishDir = file("$buildDir/repo")
|
||||||
|
|
||||||
|
|
||||||
def uploadFile(s3, bucket, path, filename) {
|
|
||||||
println "Uploading $filename to $bucket as $path"
|
|
||||||
s3.putObject(bucket, path, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
clean.doLast {
|
clean.doLast {
|
||||||
delete publishDir
|
delete publishDir
|
||||||
}
|
}
|
||||||
|
@ -114,14 +102,13 @@ if (version.endsWith("SNAPSHOT")) {
|
||||||
}
|
}
|
||||||
|
|
||||||
publish.doLast {
|
publish.doLast {
|
||||||
def AWSRoleARN = (System.getenv("AWS_ROLE_ARN") != null && System.getenv("AWS_ROLE_ARN") != "" ? System.getenv("AWS_ROLE_ARN") : null)
|
|
||||||
if (AWSRoleARN == null) throw new GradleException("AWS Role has not been configured, use the `AWS_ROLE_ARN` environment variable")
|
|
||||||
def auth = new STSAssumeRoleSessionCredentialsProvider.Builder(AWSRoleARN, "JavaBrigadierPublish").build()
|
|
||||||
def s3 = new AmazonS3Client(auth)
|
|
||||||
publishDir.eachFileRecurse {
|
publishDir.eachFileRecurse {
|
||||||
if (!it.name.contains(".xml") && !it.name.contains(".md5") && it.isFile()) {
|
if (!it.isFile()) {
|
||||||
def relPath = publishDir.toPath().relativize(it.toPath()).toFile().toString().replaceAll('\\\\', '/')
|
return
|
||||||
uploadFile(s3, "minecraft-libraries", relPath, it)
|
}
|
||||||
|
// Remove junk files
|
||||||
|
if (it.name.contains(".xml") || it.name.contains(".md5")) {
|
||||||
|
it.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
majorMinor: 1.0
|
majorMinor: 1.1
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -230,7 +231,6 @@ public class CommandDispatcher<S> {
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
forked |= context.isForked();
|
forked |= context.isForked();
|
||||||
if (child.hasNodes()) {
|
if (child.hasNodes()) {
|
||||||
foundCommand = true;
|
|
||||||
final RedirectModifier<S> modifier = context.getRedirectModifier();
|
final RedirectModifier<S> modifier = context.getRedirectModifier();
|
||||||
if (modifier == null) {
|
if (modifier == null) {
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
|
@ -247,6 +247,8 @@ public class CommandDispatcher<S> {
|
||||||
for (final S source : results) {
|
for (final S source : results) {
|
||||||
next.add(child.copyFor(source));
|
next.add(child.copyFor(source));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
foundCommand = true;
|
||||||
}
|
}
|
||||||
} catch (final CommandSyntaxException ex) {
|
} catch (final CommandSyntaxException ex) {
|
||||||
consumer.onCommandComplete(context, false, 0);
|
consumer.onCommandComplete(context, false, 0);
|
||||||
|
@ -588,12 +590,13 @@ public class CommandDispatcher<S> {
|
||||||
|
|
||||||
final String fullInput = parse.getReader().getString();
|
final String fullInput = parse.getReader().getString();
|
||||||
final String truncatedInput = fullInput.substring(0, cursor);
|
final String truncatedInput = fullInput.substring(0, cursor);
|
||||||
|
final String truncatedInputLowerCase = truncatedInput.toLowerCase(Locale.ROOT);
|
||||||
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
|
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (final CommandNode<S> node : parent.getChildren()) {
|
for (final CommandNode<S> node : parent.getChildren()) {
|
||||||
CompletableFuture<Suggestions> future = Suggestions.empty();
|
CompletableFuture<Suggestions> future = Suggestions.empty();
|
||||||
try {
|
try {
|
||||||
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
|
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, truncatedInputLowerCase, start));
|
||||||
} catch (final CommandSyntaxException ignored) {
|
} catch (final CommandSyntaxException ignored) {
|
||||||
}
|
}
|
||||||
futures[i++] = future;
|
futures[i++] = future;
|
||||||
|
|
|
@ -7,7 +7,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
|
||||||
public class StringReader implements ImmutableStringReader {
|
public class StringReader implements ImmutableStringReader {
|
||||||
private static final char SYNTAX_ESCAPE = '\\';
|
private static final char SYNTAX_ESCAPE = '\\';
|
||||||
private static final char SYNTAX_QUOTE = '"';
|
private static final char SYNTAX_DOUBLE_QUOTE = '"';
|
||||||
|
private static final char SYNTAX_SINGLE_QUOTE = '\'';
|
||||||
|
|
||||||
private final String string;
|
private final String string;
|
||||||
private int cursor;
|
private int cursor;
|
||||||
|
@ -87,6 +88,10 @@ public class StringReader implements ImmutableStringReader {
|
||||||
return c >= '0' && c <= '9' || c == '.' || c == '-';
|
return c >= '0' && c <= '9' || c == '.' || c == '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isQuotedStringStart(char c) {
|
||||||
|
return c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE;
|
||||||
|
}
|
||||||
|
|
||||||
public void skipWhitespace() {
|
public void skipWhitespace() {
|
||||||
while (canRead() && Character.isWhitespace(peek())) {
|
while (canRead() && Character.isWhitespace(peek())) {
|
||||||
skip();
|
skip();
|
||||||
|
@ -180,16 +185,22 @@ public class StringReader implements ImmutableStringReader {
|
||||||
public String readQuotedString() throws CommandSyntaxException {
|
public String readQuotedString() throws CommandSyntaxException {
|
||||||
if (!canRead()) {
|
if (!canRead()) {
|
||||||
return "";
|
return "";
|
||||||
} else if (peek() != SYNTAX_QUOTE) {
|
}
|
||||||
|
final char next = peek();
|
||||||
|
if (!isQuotedStringStart(next)) {
|
||||||
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote().createWithContext(this);
|
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerExpectedStartOfQuote().createWithContext(this);
|
||||||
}
|
}
|
||||||
skip();
|
skip();
|
||||||
|
return readStringUntil(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String readStringUntil(char terminator) throws CommandSyntaxException {
|
||||||
final StringBuilder result = new StringBuilder();
|
final StringBuilder result = new StringBuilder();
|
||||||
boolean escaped = false;
|
boolean escaped = false;
|
||||||
while (canRead()) {
|
while (canRead()) {
|
||||||
final char c = read();
|
final char c = read();
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
if (c == SYNTAX_QUOTE || c == SYNTAX_ESCAPE) {
|
if (c == terminator || c == SYNTAX_ESCAPE) {
|
||||||
result.append(c);
|
result.append(c);
|
||||||
escaped = false;
|
escaped = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -198,7 +209,7 @@ public class StringReader implements ImmutableStringReader {
|
||||||
}
|
}
|
||||||
} else if (c == SYNTAX_ESCAPE) {
|
} else if (c == SYNTAX_ESCAPE) {
|
||||||
escaped = true;
|
escaped = true;
|
||||||
} else if (c == SYNTAX_QUOTE) {
|
} else if (c == terminator) {
|
||||||
return result.toString();
|
return result.toString();
|
||||||
} else {
|
} else {
|
||||||
result.append(c);
|
result.append(c);
|
||||||
|
@ -209,11 +220,15 @@ public class StringReader implements ImmutableStringReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readString() throws CommandSyntaxException {
|
public String readString() throws CommandSyntaxException {
|
||||||
if (canRead() && peek() == SYNTAX_QUOTE) {
|
if (!canRead()) {
|
||||||
return readQuotedString();
|
return "";
|
||||||
} else {
|
|
||||||
return readUnquotedString();
|
|
||||||
}
|
}
|
||||||
|
final char next = peek();
|
||||||
|
if (isQuotedStringStart(next)) {
|
||||||
|
skip();
|
||||||
|
return readStringUntil(next);
|
||||||
|
}
|
||||||
|
return readUnquotedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean readBoolean() throws CommandSyntaxException {
|
public boolean readBoolean() throws CommandSyntaxException {
|
||||||
|
|
|
@ -34,10 +34,10 @@ public class BoolArgumentType implements ArgumentType<Boolean> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||||
if ("true".startsWith(builder.getRemaining().toLowerCase())) {
|
if ("true".startsWith(builder.getRemainingLowerCase())) {
|
||||||
builder.suggest("true");
|
builder.suggest("true");
|
||||||
}
|
}
|
||||||
if ("false".startsWith(builder.getRemaining().toLowerCase())) {
|
if ("false".startsWith(builder.getRemainingLowerCase())) {
|
||||||
builder.suggest("false");
|
builder.suggest("false");
|
||||||
}
|
}
|
||||||
return builder.buildFuture();
|
return builder.buildFuture();
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package com.mojang.brigadier.builder;
|
package com.mojang.brigadier.builder;
|
||||||
|
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.Message;
|
||||||
import com.mojang.brigadier.RedirectModifier;
|
import com.mojang.brigadier.RedirectModifier;
|
||||||
import com.mojang.brigadier.SingleRedirectModifier;
|
import com.mojang.brigadier.SingleRedirectModifier;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
@ -20,6 +21,7 @@ public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
|
||||||
private CommandNode<S> target;
|
private CommandNode<S> target;
|
||||||
private RedirectModifier<S> modifier = null;
|
private RedirectModifier<S> modifier = null;
|
||||||
private boolean forks;
|
private boolean forks;
|
||||||
|
private Message description;
|
||||||
|
|
||||||
protected abstract T getThis();
|
protected abstract T getThis();
|
||||||
|
|
||||||
|
@ -52,6 +54,15 @@ public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public T describe(final Message description) {
|
||||||
|
this.description = description;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
public T requires(final Predicate<S> requirement) {
|
public T requires(final Predicate<S> requirement) {
|
||||||
this.requirement = requirement;
|
this.requirement = requirement;
|
||||||
return getThis();
|
return getThis();
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class LiteralArgumentBuilder<S> extends ArgumentBuilder<S, LiteralArgumen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiteralCommandNode<S> build() {
|
public LiteralCommandNode<S> build() {
|
||||||
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork());
|
final LiteralCommandNode<S> result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getDescription());
|
||||||
|
|
||||||
for (final CommandNode<S> argument : getArguments()) {
|
for (final CommandNode<S> argument : getArguments()) {
|
||||||
result.addChild(argument);
|
result.addChild(argument);
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredAr
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArgumentCommandNode<S, T> build() {
|
public ArgumentCommandNode<S, T> build() {
|
||||||
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider());
|
final ArgumentCommandNode<S, T> result = new ArgumentCommandNode<>(getName(), getType(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork(), getSuggestionsProvider(), getDescription());
|
||||||
|
|
||||||
for (final CommandNode<S> argument : getArguments()) {
|
for (final CommandNode<S> argument : getArguments()) {
|
||||||
result.addChild(argument);
|
result.addChild(argument);
|
||||||
|
|
|
@ -8,18 +8,27 @@ import com.mojang.brigadier.context.StringRange;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class SuggestionsBuilder {
|
public class SuggestionsBuilder {
|
||||||
private final String input;
|
private final String input;
|
||||||
|
private final String inputLowerCase;
|
||||||
private final int start;
|
private final int start;
|
||||||
private final String remaining;
|
private final String remaining;
|
||||||
|
private final String remainingLowerCase;
|
||||||
private final List<Suggestion> result = new ArrayList<>();
|
private final List<Suggestion> result = new ArrayList<>();
|
||||||
|
|
||||||
public SuggestionsBuilder(final String input, final int start) {
|
public SuggestionsBuilder(final String input, final String inputLowerCase, final int start) {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
|
this.inputLowerCase = inputLowerCase;
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.remaining = input.substring(start);
|
this.remaining = input.substring(start);
|
||||||
|
this.remainingLowerCase = inputLowerCase.substring(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SuggestionsBuilder(final String input, final int start) {
|
||||||
|
this(input, input.toLowerCase(Locale.ROOT), start);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getInput() {
|
public String getInput() {
|
||||||
|
@ -34,6 +43,10 @@ public class SuggestionsBuilder {
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRemainingLowerCase() {
|
||||||
|
return remainingLowerCase;
|
||||||
|
}
|
||||||
|
|
||||||
public Suggestions build() {
|
public Suggestions build() {
|
||||||
return Suggestions.create(input, result);
|
return Suggestions.create(input, result);
|
||||||
}
|
}
|
||||||
|
@ -74,10 +87,10 @@ public class SuggestionsBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SuggestionsBuilder createOffset(final int start) {
|
public SuggestionsBuilder createOffset(final int start) {
|
||||||
return new SuggestionsBuilder(input, start);
|
return new SuggestionsBuilder(input, inputLowerCase, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SuggestionsBuilder restart() {
|
public SuggestionsBuilder restart() {
|
||||||
return new SuggestionsBuilder(input, start);
|
return createOffset(start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package com.mojang.brigadier.tree;
|
package com.mojang.brigadier.tree;
|
||||||
|
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.Message;
|
||||||
import com.mojang.brigadier.RedirectModifier;
|
import com.mojang.brigadier.RedirectModifier;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
@ -29,7 +30,11 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
|
||||||
private final SuggestionProvider<S> customSuggestions;
|
private final SuggestionProvider<S> customSuggestions;
|
||||||
|
|
||||||
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions) {
|
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions) {
|
||||||
super(command, requirement, redirect, modifier, forks);
|
this(name, type, command, requirement, redirect, modifier, forks, customSuggestions, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final SuggestionProvider<S> customSuggestions, final Message description) {
|
||||||
|
super(command, requirement, redirect, modifier, forks, description);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.customSuggestions = customSuggestions;
|
this.customSuggestions = customSuggestions;
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
|
|
||||||
package com.mojang.brigadier.tree;
|
package com.mojang.brigadier.tree;
|
||||||
|
|
||||||
import com.mojang.brigadier.AmbiguityConsumer;
|
import com.mojang.brigadier.*;
|
||||||
import com.mojang.brigadier.Command;
|
|
||||||
import com.mojang.brigadier.RedirectModifier;
|
|
||||||
import com.mojang.brigadier.StringReader;
|
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||||
|
@ -22,30 +19,39 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||||
private Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
||||||
private Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
||||||
private Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
||||||
private final Predicate<S> requirement;
|
private final Predicate<S> requirement;
|
||||||
private final CommandNode<S> redirect;
|
private final CommandNode<S> redirect;
|
||||||
private final RedirectModifier<S> modifier;
|
private final RedirectModifier<S> modifier;
|
||||||
private final boolean forks;
|
private final boolean forks;
|
||||||
private Command<S> command;
|
private Command<S> command;
|
||||||
|
private Message description;
|
||||||
|
|
||||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||||
|
this(command, requirement, redirect, modifier, forks, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final Message description) {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.requirement = requirement;
|
this.requirement = requirement;
|
||||||
this.redirect = redirect;
|
this.redirect = redirect;
|
||||||
this.modifier = modifier;
|
this.modifier = modifier;
|
||||||
this.forks = forks;
|
this.forks = forks;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command<S> getCommand() {
|
public Command<S> getCommand() {
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Message getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<CommandNode<S>> getChildren() {
|
public Collection<CommandNode<S>> getChildren() {
|
||||||
return children.values();
|
return children.values();
|
||||||
}
|
}
|
||||||
|
@ -88,8 +94,6 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||||
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
children = children.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package com.mojang.brigadier.tree;
|
package com.mojang.brigadier.tree;
|
||||||
|
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.Message;
|
||||||
import com.mojang.brigadier.RedirectModifier;
|
import com.mojang.brigadier.RedirectModifier;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
@ -16,15 +17,22 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class LiteralCommandNode<S> extends CommandNode<S> {
|
public class LiteralCommandNode<S> extends CommandNode<S> {
|
||||||
private final String literal;
|
private final String literal;
|
||||||
|
private final String literalLowerCase;
|
||||||
|
|
||||||
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||||
super(command, requirement, redirect, modifier, forks);
|
this(literal, command, requirement, redirect, modifier, forks, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks, final Message description) {
|
||||||
|
super(command, requirement, redirect, modifier, forks, description);
|
||||||
this.literal = literal;
|
this.literal = literal;
|
||||||
|
this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLiteral() {
|
public String getLiteral() {
|
||||||
|
@ -66,7 +74,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||||
if (literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) {
|
if (literalLowerCase.startsWith(builder.getRemainingLowerCase())) {
|
||||||
return builder.suggest(literal).buildFuture();
|
return builder.suggest(literal).buildFuture();
|
||||||
} else {
|
} else {
|
||||||
return Suggestions.empty();
|
return Suggestions.empty();
|
||||||
|
|
|
@ -17,7 +17,7 @@ import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class RootCommandNode<S> extends CommandNode<S> {
|
public class RootCommandNode<S> extends CommandNode<S> {
|
||||||
public RootCommandNode() {
|
public RootCommandNode() {
|
||||||
super(null, c -> true, null, s -> Collections.singleton(s.getSource()), false);
|
super(null, c -> true, null, s -> Collections.singleton(s.getSource()), false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package com.mojang.brigadier;
|
package com.mojang.brigadier;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
@ -14,6 +15,9 @@ import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||||
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
|
||||||
|
@ -326,6 +330,37 @@ public class CommandDispatcherTest {
|
||||||
verify(command).run(argThat(hasProperty("source", is(source2))));
|
verify(command).run(argThat(hasProperty("source", is(source2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncompleteRedirectShouldThrow() {
|
||||||
|
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
||||||
|
.then(literal("bar")
|
||||||
|
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
||||||
|
.then(literal("awa").executes(context -> 2)));
|
||||||
|
final LiteralCommandNode<Object> baz = subject.register(literal("baz").redirect(foo));
|
||||||
|
try {
|
||||||
|
int result = subject.execute("baz bar", source);
|
||||||
|
fail("Should have thrown an exception");
|
||||||
|
} catch (CommandSyntaxException e) {
|
||||||
|
assertThat(e.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRedirectModifierEmptyResult() {
|
||||||
|
final LiteralCommandNode<Object> foo = subject.register(literal("foo")
|
||||||
|
.then(literal("bar")
|
||||||
|
.then(argument("value", integer()).executes(context -> IntegerArgumentType.getInteger(context, "value"))))
|
||||||
|
.then(literal("awa").executes(context -> 2)));
|
||||||
|
final RedirectModifier<Object> emptyModifier = context -> Collections.emptyList();
|
||||||
|
final LiteralCommandNode<Object> baz = subject.register(literal("baz").fork(foo, emptyModifier));
|
||||||
|
try {
|
||||||
|
int result = subject.execute("baz bar 100", source);
|
||||||
|
assertThat(result, is(0)); // No commands executed, so result is 0
|
||||||
|
} catch (CommandSyntaxException e) {
|
||||||
|
fail("Should not throw an exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExecuteOrphanedSubcommand() throws Exception {
|
public void testExecuteOrphanedSubcommand() throws Exception {
|
||||||
subject.register(literal("foo").then(
|
subject.register(literal("foo").then(
|
||||||
|
|
|
@ -156,6 +156,30 @@ public class StringReaderTest {
|
||||||
assertThat(reader.getRemaining(), equalTo(""));
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readSingleQuotedString() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("'hello world'");
|
||||||
|
assertThat(reader.readQuotedString(), equalTo("hello world"));
|
||||||
|
assertThat(reader.getRead(), equalTo("'hello world'"));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readMixedQuotedString_doubleInsideSingle() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("'hello \"world\"'");
|
||||||
|
assertThat(reader.readQuotedString(), equalTo("hello \"world\""));
|
||||||
|
assertThat(reader.getRead(), equalTo("'hello \"world\"'"));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readMixedQuotedString_singleInsideDouble() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("\"hello 'world'\"");
|
||||||
|
assertThat(reader.readQuotedString(), equalTo("hello 'world'"));
|
||||||
|
assertThat(reader.getRead(), equalTo("\"hello 'world'\""));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readQuotedString_empty() throws Exception {
|
public void readQuotedString_empty() throws Exception {
|
||||||
final StringReader reader = new StringReader("");
|
final StringReader reader = new StringReader("");
|
||||||
|
@ -242,6 +266,40 @@ public class StringReaderTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readQuotedString_invalidQuoteEscape() throws Exception {
|
||||||
|
try {
|
||||||
|
new StringReader("'hello\\\"\'world").readQuotedString();
|
||||||
|
} catch (final CommandSyntaxException ex) {
|
||||||
|
assertThat(ex.getType(), is(CommandSyntaxException.BUILT_IN_EXCEPTIONS.readerInvalidEscape()));
|
||||||
|
assertThat(ex.getCursor(), is(7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readString_noQuotes() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("hello world");
|
||||||
|
assertThat(reader.readString(), equalTo("hello"));
|
||||||
|
assertThat(reader.getRead(), equalTo("hello"));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(" world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readString_singleQuotes() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("'hello world'");
|
||||||
|
assertThat(reader.readString(), equalTo("hello world"));
|
||||||
|
assertThat(reader.getRead(), equalTo("'hello world'"));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readString_doubleQuotes() throws Exception {
|
||||||
|
final StringReader reader = new StringReader("\"hello world\"");
|
||||||
|
assertThat(reader.readString(), equalTo("hello world"));
|
||||||
|
assertThat(reader.getRead(), equalTo("\"hello world\""));
|
||||||
|
assertThat(reader.getRemaining(), equalTo(""));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void readInt() throws Exception {
|
public void readInt() throws Exception {
|
||||||
final StringReader reader = new StringReader("1234567890");
|
final StringReader reader = new StringReader("1234567890");
|
||||||
|
|
Loading…
Reference in a new issue