Archived
0

Merge upstream

This commit is contained in:
Dico Karssiens
2019-01-10 18:52:51 +00:00
187 changed files with 13983 additions and 13504 deletions

View File

@@ -1,19 +1,19 @@
[*] [*]
charset=utf-8 charset=utf-8
end_of_line=lf end_of_line=lf
insert_final_newline=false insert_final_newline=false
indent_style=space indent_style=space
indent_size=4 indent_size=4
[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] [{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}]
indent_style=space indent_style=space
indent_size=2 indent_size=2
[{*.ddl,*.sql}] [{*.ddl,*.sql}]
indent_style=space indent_style=space
indent_size=2 indent_size=2
[{*.yml,*.yaml}] [{*.yml,*.yaml}]
indent_style=space indent_style=space
indent_size=2 indent_size=2

3
.gitignore vendored
View File

@@ -6,4 +6,5 @@ build/
/debug/ /debug/
target/ target/
/gradle-output.txt /gradle-output.txt
/*.java /*.java
*.dump

View File

@@ -1,16 +1,14 @@
# Parcels # Parcels
Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core). Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core).
Newer version of discontinued [Parcels](https://github.com/Dico200/Parcels-Java).
Newer version of the discontinued [Parcels-Java](https://github.com/Dico200/Parcels-Java). Written in Kotlin.
This project is WIP.
Written in Kotlin.
This project is WIP.
![world screenshot](https://i.imgur.com/tpbKrQI.png) ![world screenshot](https://i.imgur.com/tpbKrQI.png)
## Build ## Build
1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory 1. Run `gradle releaseJar`
2. Run `gradle releaseJar` 1. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory
3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory

View File

@@ -9,31 +9,32 @@ import java.net.URL
val stdout = PrintWriter("gradle-output.txt") val stdout = PrintWriter("gradle-output.txt")
group = "io.dico" group = "io.dico"
version = "0.2" version = "0.3"
plugins { plugins {
java java
kotlin("jvm") version "1.3.0-rc-57" kotlin("jvm") version "1.3.0"
id("com.github.johnrengelman.plugin-shadow") version "2.0.3" id("com.github.johnrengelman.plugin-shadow") version "2.0.3"
} }
allprojects { allprojects {
apply<JavaPlugin>() apply<JavaPlugin>()
apply(plugin = "idea") apply(plugin = "idea")
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots") maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots/")
maven("https://dl.bintray.com/kotlin/exposed") maven("https://dl.bintray.com/kotlin/exposed/")
maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlin-dev/")
maven("https://dl.bintray.com/kotlin/kotlin-eap/")
maven("https://dl.bintray.com/kotlin/kotlinx/") maven("https://dl.bintray.com/kotlin/kotlinx/")
maven("http://maven.sk89q.com/repo")
} }
dependencies { dependencies {
val spigotVersion = "1.13.1-R0.1-SNAPSHOT" val spigotVersion = "1.13.2-R0.1-SNAPSHOT"
c.provided("org.bukkit:bukkit:$spigotVersion") { isTransitive = false } c.provided("org.bukkit:bukkit:$spigotVersion") { isTransitive = false }
c.provided("org.spigotmc:spigot-api:$spigotVersion") { isTransitive = false } c.provided("org.spigotmc:spigot-api:$spigotVersion") { isTransitive = false }
@@ -52,13 +53,15 @@ project(":dicore3:dicore3-core") {
} }
} }
val coroutinesCore = kotlinx("coroutines-core:0.26.1-eap13")
project(":dicore3:dicore3-command") { project(":dicore3:dicore3-command") {
apply<KotlinPlatformJvmPlugin>() apply<KotlinPlatformJvmPlugin>()
dependencies { dependencies {
c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("stdlib-jdk8"))
c.kotlinStd(kotlin("reflect")) c.kotlinStd(kotlin("reflect"))
c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) c.kotlinStd(coroutinesCore)
compile(project(":dicore3:dicore3-core")) compile(project(":dicore3:dicore3-core"))
compile("com.thoughtworks.paranamer:paranamer:2.8") compile("com.thoughtworks.paranamer:paranamer:2.8")
@@ -72,12 +75,13 @@ dependencies {
c.kotlinStd(kotlin("stdlib-jdk8")) c.kotlinStd(kotlin("stdlib-jdk8"))
c.kotlinStd(kotlin("reflect")) c.kotlinStd(kotlin("reflect"))
c.kotlinStd(kotlinx("coroutines-core:0.26.1-eap13")) c.kotlinStd(coroutinesCore)
c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.7-eap13") c.kotlinStd("org.jetbrains.kotlinx:atomicfu-common:0.11.12")
// not on sk89q maven repo yet // not on sk89q maven repo yet
compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar")) //compileClasspath(files("$rootDir/debug/plugins/worldedit-bukkit-7.0.0-beta-01.jar"))
compileClasspath(files("$rootDir/debug/lib/spigot-1.13.1.jar")) // compileClasspath(files("$rootDir/debug/lib/spigot-1.13.2.jar"))
compileClasspath("com.sk89q.worldedit:worldedit-bukkit:7.0.0-SNAPSHOT")
compile("org.jetbrains.exposed:exposed:0.10.5") { isTransitive = false } compile("org.jetbrains.exposed:exposed:0.10.5") { isTransitive = false }
compile("joda-time:joda-time:2.10") compile("joda-time:joda-time:2.10")
@@ -137,7 +141,7 @@ tasks {
// spigot ships a later version in the root, so we must relocate ours // spigot ships a later version in the root, so we must relocate ours
relocate("org.yaml.snakeyaml.", "io.dico.parcels2.lib.org.yaml.snakeyaml.") relocate("org.yaml.snakeyaml.", "io.dico.parcels2.lib.org.yaml.snakeyaml.")
manifest.attributes["Class-Path"] = "../lib/kotlin-stdlib.jar" manifest.attributes["Class-Path"] = "../lib/kotlin-stdlib.jar ../lib/mariadb-java-client-2.2.6.jar"
dependsOn(kotlinStdlibJar) dependsOn(kotlinStdlibJar)
} }
@@ -146,11 +150,6 @@ tasks {
val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar") val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar")
val serverJarFile = file("$serverDir/lib/spigot.jar") val serverJarFile = file("$serverDir/lib/spigot.jar")
doFirst {
}
} }
} }
@@ -171,5 +170,7 @@ val ConfigurationContainer.`provided`: Configuration
val ConfigurationContainer.`kotlinStd`: Configuration val ConfigurationContainer.`kotlinStd`: Configuration
get() = findByName("kotlinStd") ?: create("kotlinStd").let { compileClasspath.extendsFrom(it) } get() = findByName("kotlinStd") ?: create("kotlinStd").let { compileClasspath.extendsFrom(it) }
fun Jar.fromFiles(files: Iterable<File>) = fun Jar.fromFiles(files: Iterable<File>) {
afterEvaluate { from(*files.map { if (it.isDirectory) it else zipTree(it) }.toTypedArray()) } afterEvaluate { from(*files.map { if (it.isDirectory) it else zipTree(it) }.toTypedArray()) }
}

View File

@@ -1,120 +1,120 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.predef.DefaultGroupCommand; import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.predef.HelpCommand; import io.dico.dicore.command.predef.HelpCommand;
import java.util.*; import java.util.*;
public class ChildCommandAddress extends ModifiableCommandAddress { public class ChildCommandAddress extends ModifiableCommandAddress {
ModifiableCommandAddress parent; ModifiableCommandAddress parent;
final List<String> namesModifiable = new ArrayList<>(4); final List<String> namesModifiable = new ArrayList<>(4);
List<String> names = namesModifiable; List<String> names = namesModifiable;
Command command; Command command;
boolean isCommandTrailing; boolean isCommandTrailing;
public ChildCommandAddress() { public ChildCommandAddress() {
} }
public ChildCommandAddress(Command command) { public ChildCommandAddress(Command command) {
this.command = command; this.command = command;
} }
public ChildCommandAddress(Command command, String name, String... aliases) { public ChildCommandAddress(Command command, String name, String... aliases) {
this(command); this(command);
addNameAndAliases(name, aliases); addNameAndAliases(name, aliases);
} }
public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) { public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) {
ChildCommandAddress rv = new ChildCommandAddress(); ChildCommandAddress rv = new ChildCommandAddress();
rv.setupAsPlaceholder(name, aliases); rv.setupAsPlaceholder(name, aliases);
return rv; return rv;
} }
public void setupAsPlaceholder(String name, String... aliases) { public void setupAsPlaceholder(String name, String... aliases) {
if (!hasCommand()) { if (!hasCommand()) {
setCommand(DefaultGroupCommand.getInstance()); setCommand(DefaultGroupCommand.getInstance());
} }
addNameAndAliases(name, aliases); addNameAndAliases(name, aliases);
HelpCommand.registerAsChild(this); HelpCommand.registerAsChild(this);
} }
@Override @Override
public boolean isRoot() { public boolean isRoot() {
return false; return false;
} }
@Override @Override
public ModifiableCommandAddress getParent() { public ModifiableCommandAddress getParent() {
return parent; return parent;
} }
@Override @Override
public Command getCommand() { public Command getCommand() {
return command; return command;
} }
@Override @Override
public void setCommand(Command command) { public void setCommand(Command command) {
if (hasUserDeclaredCommand()) { if (hasUserDeclaredCommand()) {
throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\""); throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\"");
} }
this.command = command; this.command = command;
} }
@Override @Override
public List<String> getNames() { public List<String> getNames() {
return names; return names;
} }
public void addNameAndAliases(String name, String... aliases) { public void addNameAndAliases(String name, String... aliases) {
names.add(name); names.add(name);
names.addAll(Arrays.asList(aliases)); names.addAll(Arrays.asList(aliases));
} }
@Override @Override
public String getMainKey() { public String getMainKey() {
return namesModifiable.isEmpty() ? null : namesModifiable.get(0); return namesModifiable.isEmpty() ? null : namesModifiable.get(0);
} }
@Override @Override
public String getAddress() { public String getAddress() {
ICommandAddress address = this; ICommandAddress address = this;
int depth = getDepth(); int depth = getDepth();
String[] keys = new String[depth]; String[] keys = new String[depth];
for (int i = depth - 1; i >= 0; i--) { for (int i = depth - 1; i >= 0; i--) {
keys[i] = address.getMainKey(); keys[i] = address.getMainKey();
address = address.getParent(); address = address.getParent();
} }
return String.join(" ", keys); return String.join(" ", keys);
} }
public void finalizeNames() { public void finalizeNames() {
if (names == namesModifiable) { if (names == namesModifiable) {
names = Collections.unmodifiableList(namesModifiable); names = Collections.unmodifiableList(namesModifiable);
} }
} }
Iterator<String> modifiableNamesIterator() { Iterator<String> modifiableNamesIterator() {
return namesModifiable.iterator(); return namesModifiable.iterator();
} }
void setParent(ModifiableCommandAddress parent) { void setParent(ModifiableCommandAddress parent) {
finalizeNames(); finalizeNames();
this.parent = parent; this.parent = parent;
} }
@Override @Override
public boolean isCommandTrailing() { public boolean isCommandTrailing() {
return isCommandTrailing; return isCommandTrailing;
} }
@Override @Override
public void setCommandTrailing(boolean trailing) { public void setCommandTrailing(boolean trailing) {
if (hasChildren()) { if (hasChildren()) {
throw new IllegalStateException("Address already has children, this property can't be modified"); throw new IllegalStateException("Address already has children, this property can't be modified");
} }
isCommandTrailing = trailing; isCommandTrailing = trailing;
} }
} }

View File

@@ -1,158 +1,158 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.IContextFilter.Priority; import io.dico.dicore.command.IContextFilter.Priority;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.IArgumentPreProcessor; import io.dico.dicore.command.parameter.IArgumentPreProcessor;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.ParameterList; import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.parameter.type.ParameterType; import io.dico.dicore.command.parameter.type.ParameterType;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public abstract class Command { public abstract class Command {
private static final String[] EMPTY_DESCRIPTION = new String[0]; private static final String[] EMPTY_DESCRIPTION = new String[0];
private final ParameterList parameterList = new ParameterList(); private final ParameterList parameterList = new ParameterList();
private final List<IContextFilter> contextFilters = new ArrayList<>(3); private final List<IContextFilter> contextFilters = new ArrayList<>(3);
private String[] description = EMPTY_DESCRIPTION; private String[] description = EMPTY_DESCRIPTION;
private String shortDescription; private String shortDescription;
public Command addParameter(Parameter<?, ?> parameter) { public Command addParameter(Parameter<?, ?> parameter) {
parameterList.addParameter(parameter); parameterList.addParameter(parameter);
return this; return this;
} }
public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) { public <TType> Command addParameter(String name, String description, ParameterType<TType, Void> type) {
return addParameter(new Parameter<>(name, description, type, null, false, null)); return addParameter(new Parameter<>(name, description, type, null, false, null));
} }
public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) { public <TType, TParamInfo> Command addParameter(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
return addParameter(new Parameter<>(name, description, type, paramInfo, false, null)); return addParameter(new Parameter<>(name, description, type, paramInfo, false, null));
} }
public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) { public <TType> Command addFlag(String name, String description, ParameterType<TType, Void> type) {
return addParameter(new Parameter<>('-' + name, description, type, null, true, null)); return addParameter(new Parameter<>('-' + name, description, type, null, true, null));
} }
public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) { public <TType, TParamInfo> Command addFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo) {
return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null)); return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null));
} }
public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) { public <TType> Command addAuthorizedFlag(String name, String description, ParameterType<TType, Void> type, String permission) {
return addParameter(new Parameter<>('-' + name, description, type, null, true, permission)); return addParameter(new Parameter<>('-' + name, description, type, null, true, permission));
} }
public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) { public <TType, TParamInfo> Command addAuthorizedFlag(String name, String description, ParameterType<TType, TParamInfo> type, TParamInfo paramInfo, String permission) {
return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission)); return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission));
} }
public Command requiredParameters(int requiredParameters) { public Command requiredParameters(int requiredParameters) {
parameterList.setRequiredCount(requiredParameters); parameterList.setRequiredCount(requiredParameters);
return this; return this;
} }
public Command repeatFinalParameter() { public Command repeatFinalParameter() {
parameterList.setRepeatFinalParameter(true); parameterList.setRepeatFinalParameter(true);
return this; return this;
} }
public Command setDescription(String... description) { public Command setDescription(String... description) {
this.description = Objects.requireNonNull(description); this.description = Objects.requireNonNull(description);
return this; return this;
} }
public Command setShortDescription(String shortDescription) { public Command setShortDescription(String shortDescription) {
this.shortDescription = shortDescription; this.shortDescription = shortDescription;
return this; return this;
} }
/* /*
public Command preprocessArguments(IArgumentPreProcessor processor) { public Command preprocessArguments(IArgumentPreProcessor processor) {
parameterList.setArgumentPreProcessor(processor); parameterList.setArgumentPreProcessor(processor);
return this; return this;
}*/ }*/
public final ParameterList getParameterList() { public final ParameterList getParameterList() {
return parameterList; return parameterList;
} }
public final String[] getDescription() { public final String[] getDescription() {
return description.length == 0 ? description : description.clone(); return description.length == 0 ? description : description.clone();
} }
public String getShortDescription() { public String getShortDescription() {
return shortDescription; return shortDescription;
} }
/** /**
* ---- CONTEXT FILTERS ---- * ---- CONTEXT FILTERS ----
* Filter the contexts. For example, if the sender must be a player but it's the console, * Filter the contexts. For example, if the sender must be a player but it's the console,
* throw a CommandException describing the problem. * throw a CommandException describing the problem.
*/ */
private transient int postParameterFilterCount = 0; private transient int postParameterFilterCount = 0;
public Command addContextFilter(IContextFilter contextFilter) { public Command addContextFilter(IContextFilter contextFilter) {
Objects.requireNonNull(contextFilter); Objects.requireNonNull(contextFilter);
if (!contextFilters.contains(contextFilter)) { if (!contextFilters.contains(contextFilter)) {
contextFilters.add(contextFilter); contextFilters.add(contextFilter);
contextFilters.sort(null); contextFilters.sort(null);
if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
postParameterFilterCount++; postParameterFilterCount++;
} }
} }
return this; return this;
} }
public List<IContextFilter> getContextFilters() { public List<IContextFilter> getContextFilters() {
return Collections.unmodifiableList(contextFilters); return Collections.unmodifiableList(contextFilters);
} }
public Command removeContextFilter(IContextFilter contextFilter) { public Command removeContextFilter(IContextFilter contextFilter) {
boolean ret = contextFilters.remove(contextFilter); boolean ret = contextFilters.remove(contextFilter);
if (ret) { if (ret) {
if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) {
postParameterFilterCount--; postParameterFilterCount--;
} }
} }
return this; return this;
} }
// ---- CONTROL FLOW IN COMMAND TREES ---- // ---- CONTROL FLOW IN COMMAND TREES ----
public boolean isVisibleTo(CommandSender sender) { public boolean isVisibleTo(CommandSender sender) {
return true; return true;
} }
public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) { public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) {
return false; return false;
} }
// ---- EXECUTION ---- // ---- EXECUTION ----
public void initializeAndFilterContext(ExecutionContext context) throws CommandException { public void initializeAndFilterContext(ExecutionContext context) throws CommandException {
int i, n; int i, n;
for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) { for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) {
contextFilters.get(i).filterContext(context); contextFilters.get(i).filterContext(context);
} }
context.parse(parameterList); context.parse(parameterList);
if (!context.isTabComplete()) { if (!context.isTabComplete()) {
for (n = contextFilters.size(); i < n; i++) { for (n = contextFilters.size(); i < n; i++) {
contextFilters.get(i).filterContext(context); contextFilters.get(i).filterContext(context);
} }
} }
} }
public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException; public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException;
public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) { public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
return context.getSuggestedCompletions(location); return context.getSuggestedCompletions(location);
} }
} }

View File

@@ -1,432 +1,432 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.chat.IChatHandler; import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.parameter.type.IParameterTypeSelector; import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector;
import io.dico.dicore.command.parameter.type.ParameterType; import io.dico.dicore.command.parameter.type.ParameterType;
import io.dico.dicore.command.predef.HelpCommand; import io.dico.dicore.command.predef.HelpCommand;
import io.dico.dicore.command.predef.PredefinedCommand; import io.dico.dicore.command.predef.PredefinedCommand;
import io.dico.dicore.command.predef.SyntaxCommand; import io.dico.dicore.command.predef.SyntaxCommand;
import io.dico.dicore.command.registration.reflect.ReflectiveRegistration; import io.dico.dicore.command.registration.reflect.ReflectiveRegistration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* Mimic of WorldEdit's CommandGraph * Mimic of WorldEdit's CommandGraph
*/ */
public final class CommandBuilder { public final class CommandBuilder {
private final RootCommandAddress root; private final RootCommandAddress root;
private ModifiableCommandAddress cur; private ModifiableCommandAddress cur;
private IParameterTypeSelector selector; private IParameterTypeSelector selector;
/** /**
* Instantiate a new CommandBuilder with a new command root system * Instantiate a new CommandBuilder with a new command root system
* Commands registered to this command builder might interfere with * Commands registered to this command builder might interfere with
* commands registered to other commands builders or by other plugins. * commands registered to other commands builders or by other plugins.
*/ */
public CommandBuilder() { public CommandBuilder() {
this(new RootCommandAddress()); this(new RootCommandAddress());
} }
/** /**
* Instantiate a new CommandBuilder with a specified root address. * Instantiate a new CommandBuilder with a specified root address.
* If the root address is identical to that of another command builder, * If the root address is identical to that of another command builder,
* they will modify the same tree. * they will modify the same tree.
* *
* @param root the root address * @param root the root address
*/ */
public CommandBuilder(RootCommandAddress root) { public CommandBuilder(RootCommandAddress root) {
this.root = Objects.requireNonNull(root); this.root = Objects.requireNonNull(root);
this.cur = root; this.cur = root;
this.selector = new MapBasedParameterTypeSelector(true); this.selector = new MapBasedParameterTypeSelector(true);
} }
/** /**
* Add a sub command at the current address * Add a sub command at the current address
* The current address can be inspected using {@link #getAddress()} * The current address can be inspected using {@link #getAddress()}
* *
* @param name the name of the command * @param name the name of the command
* @param command the command executor * @param command the command executor
* @param aliases any aliases * @param aliases any aliases
* @return this * @return this
*/ */
public CommandBuilder addSubCommand(String name, Command command, String... aliases) { public CommandBuilder addSubCommand(String name, Command command, String... aliases) {
ChildCommandAddress address = new ChildCommandAddress(command); ChildCommandAddress address = new ChildCommandAddress(command);
address.addNameAndAliases(name, aliases); address.addNameAndAliases(name, aliases);
return addSubCommand(address); return addSubCommand(address);
} }
/** /**
* Add a subcommand as an address at the current address * Add a subcommand as an address at the current address
* The result of this call is the same as * The result of this call is the same as
* {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))} * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))}
* *
* @param address the address * @param address the address
* @return this * @return this
* @throws IllegalArgumentException if {@code address.isRoot()} * @throws IllegalArgumentException if {@code address.isRoot()}
*/ */
public CommandBuilder addSubCommand(ICommandAddress address) { public CommandBuilder addSubCommand(ICommandAddress address) {
cur.addChild(address); cur.addChild(address);
return this; return this;
} }
/** /**
* Search the given class for any (static) methods using command annotations * Search the given class for any (static) methods using command annotations
* The class gets a localized parameter type selector if it defines parameter types. * The class gets a localized parameter type selector if it defines parameter types.
* Any commands found are registered as sub commands to the current address. * Any commands found are registered as sub commands to the current address.
* *
* @param clazz the clazz * @param clazz the clazz
* @return this * @return this
* @throws IllegalArgumentException if an exception occurs while parsing the methods of this class * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
* @see #registerCommands(Class, Object) * @see #registerCommands(Class, Object)
*/ */
public CommandBuilder registerCommands(Class<?> clazz) { public CommandBuilder registerCommands(Class<?> clazz) {
return registerCommands(clazz, null); return registerCommands(clazz, null);
} }
/** /**
* Search the given object's class for methods using command annotations. * Search the given object's class for methods using command annotations.
* If the object is null, only static methods are checked. Otherwise, instance methods are also checked. * If the object is null, only static methods are checked. Otherwise, instance methods are also checked.
* The class gets a localized parameter type selector if it defines parameter types. * The class gets a localized parameter type selector if it defines parameter types.
* Any commands found are registered as sub commands to the current address. * Any commands found are registered as sub commands to the current address.
* *
* @param object the object * @param object the object
* @return this * @return this
* @throws IllegalArgumentException if an exception occurs while parsing the methods of this class * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class
* @see #registerCommands(Class, Object) * @see #registerCommands(Class, Object)
*/ */
public CommandBuilder registerCommands(Object object) { public CommandBuilder registerCommands(Object object) {
return registerCommands(object.getClass(), object); return registerCommands(object.getClass(), object);
} }
/** /**
* Search the given class for methods using command annotations. * Search the given class for methods using command annotations.
* The class gets a localized parameter type selector if it defines parameter types. * The class gets a localized parameter type selector if it defines parameter types.
* Any commands found are registered as sub commands to the current address. * Any commands found are registered as sub commands to the current address.
* The instance is used to invoke non-static methods. * The instance is used to invoke non-static methods.
* *
* @param clazz the class * @param clazz the class
* @param instance the instance, null if only static methods * @param instance the instance, null if only static methods
* @return this * @return this
* @throws IllegalArgumentException if instance is not null and it's not an instance of the class * @throws IllegalArgumentException if instance is not null and it's not an instance of the class
* @throws IllegalArgumentException if another exception occurs while parsing the methods of this class * @throws IllegalArgumentException if another exception occurs while parsing the methods of this class
*/ */
public CommandBuilder registerCommands(Class<?> clazz, Object instance) { public CommandBuilder registerCommands(Class<?> clazz, Object instance) {
try { try {
ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance); ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance);
return this; return this;
} catch (Exception ex) { } catch (Exception ex) {
throw new IllegalArgumentException(ex); throw new IllegalArgumentException(ex);
} }
} }
/** /**
* register the {@link HelpCommand} as a sub command at the current address * register the {@link HelpCommand} as a sub command at the current address
* *
* @return this * @return this
*/ */
public CommandBuilder registerHelpCommand() { public CommandBuilder registerHelpCommand() {
HelpCommand.registerAsChild(cur); HelpCommand.registerAsChild(cur);
return this; return this;
} }
/** /**
* register the {@link SyntaxCommand} as a sub command a the current address * register the {@link SyntaxCommand} as a sub command a the current address
* *
* @return this * @return this
*/ */
public CommandBuilder registerSyntaxCommand() { public CommandBuilder registerSyntaxCommand() {
SyntaxCommand.registerAsChild(cur); SyntaxCommand.registerAsChild(cur);
return this; return this;
} }
/** /**
* Generate the predefined commands. * Generate the predefined commands.
* These are presets. * These are presets.
* Examples include {@code help} and {@code syntax}. * Examples include {@code help} and {@code syntax}.
* <p> * <p>
* Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)} * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)}
* *
* @param commands the commands * @param commands the commands
* @return this * @return this
*/ */
public CommandBuilder generatePredefinedCommands(String... commands) { public CommandBuilder generatePredefinedCommands(String... commands) {
ReflectiveRegistration.generateCommands(cur, commands); ReflectiveRegistration.generateCommands(cur, commands);
return this; return this;
} }
/** /**
* Unregister any childs present at the given keys. * Unregister any childs present at the given keys.
* <p> * <p>
* This method can be used to remove unwanted keys, that might have been added * This method can be used to remove unwanted keys, that might have been added
* outside of your control. For example, because you didn't want all commands * outside of your control. For example, because you didn't want all commands
* registered by {@link #registerCommands(Class, Object)}, or because you didn't * registered by {@link #registerCommands(Class, Object)}, or because you didn't
* want the help command registered by {@link #group(String, String...)} * want the help command registered by {@link #group(String, String...)}
* *
* @param removeAliases true if any aliases of the children present at the keys should be removed * @param removeAliases true if any aliases of the children present at the keys should be removed
* @param keys a varargs array containing the keys * @param keys a varargs array containing the keys
* @return this * @return this
* @throws IllegalArgumentException if keys array is empty * @throws IllegalArgumentException if keys array is empty
*/ */
public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) { public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) {
cur.removeChildren(removeAliases, keys); cur.removeChildren(removeAliases, keys);
return this; return this;
} }
/** /**
* Jump to the sub-address with the given name as main key. * Jump to the sub-address with the given name as main key.
* If an address with the exact name as main key exists, * If an address with the exact name as main key exists,
* that address becomes the current address. * that address becomes the current address.
* <p> * <p>
* Otherwise, a new addresses is registered with the name and aliases. * Otherwise, a new addresses is registered with the name and aliases.
* New addresses registered by this command have a HelpCommand added by default. * New addresses registered by this command have a HelpCommand added by default.
* <p> * <p>
* After this call, any registered commands are registered as a sub command * After this call, any registered commands are registered as a sub command
* to the new address. To restore the previous state, a call to {@link #parent()} * to the new address. To restore the previous state, a call to {@link #parent()}
* should be made. * should be made.
* <p> * <p>
* If the address is the target of a command, it will provide information about its sub commands * If the address is the target of a command, it will provide information about its sub commands
* using the HelpCommand. * using the HelpCommand.
* *
* @param name the main key * @param name the main key
* @param aliases the aliases * @param aliases the aliases
* @return this * @return this
*/ */
public CommandBuilder group(String name, String... aliases) { public CommandBuilder group(String name, String... aliases) {
ChildCommandAddress address = cur.getChild(name); ChildCommandAddress address = cur.getChild(name);
if (address == null || !name.equals(address.getMainKey())) { if (address == null || !name.equals(address.getMainKey())) {
address = new ChildCommandAddress(); address = new ChildCommandAddress();
address.setupAsPlaceholder(name, aliases); address.setupAsPlaceholder(name, aliases);
cur.addChild(address); cur.addChild(address);
} }
cur = address; cur = address;
return this; return this;
} }
/** /**
* Similar to {@link #group(String, String[])} but this will force overwrite any present group, * Similar to {@link #group(String, String[])} but this will force overwrite any present group,
* using the address passed. The address MUST be an instance of {@link ChildCommandAddress}. * using the address passed. The address MUST be an instance of {@link ChildCommandAddress}.
* *
* <p>The address must not have a parent or any keys</p> * <p>The address must not have a parent or any keys</p>
* *
* @param address the address object to use * @param address the address object to use
* @param name the main key * @param name the main key
* @param aliases any aliases * @param aliases any aliases
* @return this * @return this
* @throws IllegalArgumentException if any of the requirements set out above aren't met * @throws IllegalArgumentException if any of the requirements set out above aren't met
*/ */
public CommandBuilder group(ICommandAddress address, String name, String... aliases) { public CommandBuilder group(ICommandAddress address, String name, String... aliases) {
if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) { if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
ChildCommandAddress asChild = (ChildCommandAddress) address; ChildCommandAddress asChild = (ChildCommandAddress) address;
asChild.setupAsPlaceholder(name, aliases); asChild.setupAsPlaceholder(name, aliases);
cur.addChild(address); cur.addChild(address);
cur = asChild; cur = asChild;
return this; return this;
} }
/** /**
* Sets the description of a group created by {@link #group(String, String...)} * Sets the description of a group created by {@link #group(String, String...)}
* Can be called subsequently to making a call to {@link #group(String, String...)} * Can be called subsequently to making a call to {@link #group(String, String...)}
* *
* @param shortDescription a short description * @param shortDescription a short description
* @param description the lines of a full description. * @param description the lines of a full description.
* @return this * @return this
* @throws IllegalStateException if the current group has no command * @throws IllegalStateException if the current group has no command
*/ */
public CommandBuilder setGroupDescription(String shortDescription, String... description) { public CommandBuilder setGroupDescription(String shortDescription, String... description) {
Command command = cur.getCommand(); Command command = cur.getCommand();
if (command == null) throw new IllegalStateException(); if (command == null) throw new IllegalStateException();
cur.setCommand(command cur.setCommand(command
.setShortDescription(shortDescription) .setShortDescription(shortDescription)
.setDescription(description)); .setDescription(description));
return this; return this;
} }
/** /**
* Add a context filter to the command of the current group * Add a context filter to the command of the current group
* @return this * @return this
* @throws IllegalStateException if the current group has no command * @throws IllegalStateException if the current group has no command
*/ */
public CommandBuilder addContextFilter(IContextFilter contextFilter) { public CommandBuilder addContextFilter(IContextFilter contextFilter) {
Command command = cur.getCommand(); Command command = cur.getCommand();
if (command == null) throw new IllegalStateException(); if (command == null) throw new IllegalStateException();
cur.setCommand(command cur.setCommand(command
.addContextFilter(contextFilter)); .addContextFilter(contextFilter));
return this; return this;
} }
/** /**
* Add a required permission to the command of the current group * Add a required permission to the command of the current group
* @return this * @return this
* @throws IllegalStateException if the current group has no command * @throws IllegalStateException if the current group has no command
*/ */
public CommandBuilder addPermission(String permission) { public CommandBuilder addPermission(String permission) {
return addContextFilter(IContextFilter.permission(permission)); return addContextFilter(IContextFilter.permission(permission));
} }
/** /**
* Add a required permission to the command of the current group, which can be inherited * Add a required permission to the command of the current group, which can be inherited
* @return this * @return this
* @throws IllegalStateException if the current group has no command * @throws IllegalStateException if the current group has no command
*/ */
public CommandBuilder addInheritablePermission(String permission) { public CommandBuilder addInheritablePermission(String permission) {
return addContextFilter(IContextFilter.inheritablePermission(permission)); return addContextFilter(IContextFilter.inheritablePermission(permission));
} }
/** /**
* Jump up a level in the address * Jump up a level in the address
* *
* @return this * @return this
* @throws IllegalStateException if the address is empty * @throws IllegalStateException if the address is empty
* // has a depth of 0 // is at level 0 * // has a depth of 0 // is at level 0
*/ */
public CommandBuilder parent() { public CommandBuilder parent() {
if (cur.hasParent()) { if (cur.hasParent()) {
cur = cur.getParent(); cur = cur.getParent();
return this; return this;
} }
throw new IllegalStateException("No parent exists at this address"); throw new IllegalStateException("No parent exists at this address");
} }
/** /**
* Jump to the root (empty) address, * Jump to the root (empty) address,
* such that a subsequent call to {@link #parent()} * such that a subsequent call to {@link #parent()}
* will throw a {@link IllegalStateException} * will throw a {@link IllegalStateException}
* *
* @return this * @return this
*/ */
public CommandBuilder root() { public CommandBuilder root() {
cur = root; cur = root;
return this; return this;
} }
/** /**
* Get the current address, as a space-separated string * Get the current address, as a space-separated string
* *
* @return the current address * @return the current address
*/ */
public String getAddress() { public String getAddress() {
return cur.getAddress(); return cur.getAddress();
} }
/** /**
* Get the depth of the current address. * Get the depth of the current address.
* This is equivalent to {@code getAddress().split(" ").length}. * This is equivalent to {@code getAddress().split(" ").length}.
* If the address is empty, the depth is 0. * If the address is empty, the depth is 0.
* *
* @return the depth * @return the depth
*/ */
public int getDepth() { public int getDepth() {
return cur.getDepth(); return cur.getDepth();
} }
/** /**
* Set the command at the current group. The command is set * Set the command at the current group. The command is set
* a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)} * a level higher than it would be if this were a call to {@link #addSubCommand(String, Command, String...)}
* <p> * <p>
* If a call to {@link #setGroupDescription(String, String...)} was made at the same address before, * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before,
* the description is copied to the given executor. * the description is copied to the given executor.
* *
* @param command the executor * @param command the executor
* @return this * @return this
* @throws IllegalArgumentException if the command at the address is present and declared by the user, * @throws IllegalArgumentException if the command at the address is present and declared by the user,
* in other words, it's not a {@link PredefinedCommand} * in other words, it's not a {@link PredefinedCommand}
*/ */
public CommandBuilder setCommand(Command command) { public CommandBuilder setCommand(Command command) {
Command current = cur.getCommand(); Command current = cur.getCommand();
if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) { if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) {
command.setShortDescription(current.getShortDescription()); command.setShortDescription(current.getShortDescription());
command.setDescription(current.getDescription()); command.setDescription(current.getDescription());
} }
cur.setCommand(command); cur.setCommand(command);
return this; return this;
} }
/** /**
* Configure the chat handler at this address. The chat handler * Configure the chat handler at this address. The chat handler
* is used for all children down the tree if they don't explicitly have * is used for all children down the tree if they don't explicitly have
* their own chat handler configured. If this isn't configured, * their own chat handler configured. If this isn't configured,
* {@code ChatHandlers.defaultChat()} is used. * {@code ChatHandlers.defaultChat()} is used.
* *
* @param chatHandler the chat handler * @param chatHandler the chat handler
* @return this * @return this
*/ */
public CommandBuilder setChatHandler(IChatHandler chatHandler) { public CommandBuilder setChatHandler(IChatHandler chatHandler) {
cur.setChatHandler(chatHandler); cur.setChatHandler(chatHandler);
return this; return this;
} }
/** /**
* Add the parameter type to this builder's selector. * Add the parameter type to this builder's selector.
* *
* @param type the type * @param type the type
* @param <T> the return type of the parameter type * @param <T> the return type of the parameter type
* @return this * @return this
*/ */
public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) { public <T> CommandBuilder addParameterType(ParameterType<T, Void> type) {
selector.addType(false, type); selector.addType(false, type);
return this; return this;
} }
/** /**
* Add the parameter type to this builder's selector. * Add the parameter type to this builder's selector.
* *
* @param infolessAlias whether to also register the type with an infoless alias. * @param infolessAlias whether to also register the type with an infoless alias.
* this increases the priority assigned to the type if no info object is present. * this increases the priority assigned to the type if no info object is present.
* @param type the type * @param type the type
* @param <T> the return type of the parameter type * @param <T> the return type of the parameter type
* @param <C> the parameter config type (info object) * @param <C> the parameter config type (info object)
* @return this * @return this
*/ */
public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) { public <T, C> CommandBuilder addParameterType(boolean infolessAlias, ParameterType<T, C> type) {
selector.addType(infolessAlias, type); selector.addType(infolessAlias, type);
return this; return this;
} }
/** /**
* Get the dispatcher for the root address. * Get the dispatcher for the root address.
* The dispatcher should be used to finally register all commands, * The dispatcher should be used to finally register all commands,
* after they are all declared. * after they are all declared.
* *
* @return the dispatcher * @return the dispatcher
*/ */
public ICommandDispatcher getDispatcher() { public ICommandDispatcher getDispatcher() {
return root; return root;
} }
/** /**
* Print debugging information about the current addresses and commands in this builder * Print debugging information about the current addresses and commands in this builder
* A StackTraceElement indicating where this was called from is also included * A StackTraceElement indicating where this was called from is also included
* *
* @return this * @return this
*/ */
public CommandBuilder printDebugInformation() { public CommandBuilder printDebugInformation() {
String address = cur == root ? "<root>" : cur.getAddress(); String address = cur == root ? "<root>" : cur.getAddress();
StackTraceElement caller = getCallsite(); StackTraceElement caller = getCallsite();
StringBuilder message = new StringBuilder("### CommandBuilder dump ###"); StringBuilder message = new StringBuilder("### CommandBuilder dump ###");
message.append("\nCalled from ").append(caller); message.append("\nCalled from ").append(caller);
message.append("\nPosition: ").append(address); message.append("\nPosition: ").append(address);
cur.appendDebugInformation(message, "", new HashSet<>()); cur.appendDebugInformation(message, "", new HashSet<>());
System.out.println(message); System.out.println(message);
return this; return this;
} }
private static StackTraceElement getCallsite() { private static StackTraceElement getCallsite() {
// [0] Thread.currentThread() // [0] Thread.currentThread()
// [1] CommandBuilder.getCallsite() // [1] CommandBuilder.getCallsite()
// [2] Calling method // [2] Calling method
// [3] Method calling the calling method // [3] Method calling the calling method
StackTraceElement[] trace = Thread.currentThread().getStackTrace(); StackTraceElement[] trace = Thread.currentThread().getStackTrace();
return trace.length > 3 ? trace[3] : null; return trace.length > 3 ? trace[3] : null;
} }
} }

View File

@@ -1,28 +1,28 @@
package io.dico.dicore.command; package io.dico.dicore.command;
public class CommandException extends Exception { public class CommandException extends Exception {
public CommandException() { public CommandException() {
} }
public CommandException(String message) { public CommandException(String message) {
super(message); super(message);
} }
public CommandException(String message, Throwable cause) { public CommandException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public CommandException(Throwable cause) { public CommandException(Throwable cause) {
super(cause); super(cause);
} }
public static CommandException missingArgument(String parameterName) { public static CommandException missingArgument(String parameterName) {
return new CommandException("Missing argument for " + parameterName); return new CommandException("Missing argument for " + parameterName);
} }
public static CommandException invalidArgument(String parameterName, String syntaxHelp) { public static CommandException invalidArgument(String parameterName, String syntaxHelp) {
return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp); return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp);
} }
} }

View File

@@ -1,19 +1,19 @@
package io.dico.dicore.command; package io.dico.dicore.command;
public enum EMessageType { public enum EMessageType {
GOOD_NEWS, GOOD_NEWS,
BAD_NEWS, BAD_NEWS,
NEUTRAL, NEUTRAL,
INFORMATIVE, INFORMATIVE,
WARNING, WARNING,
INSTRUCTION, INSTRUCTION,
EXCEPTION, EXCEPTION,
RESULT, RESULT,
CUSTOM, CUSTOM,
DESCRIPTION, DESCRIPTION,
SYNTAX, SYNTAX,
HIGHLIGHT, HIGHLIGHT,
SUBCOMMAND, SUBCOMMAND,
NUMBER, NUMBER,
} }

View File

@@ -1,12 +1,12 @@
package io.dico.dicore.command; package io.dico.dicore.command;
/** /**
* Override policies for registering to the command map * Override policies for registering to the command map
*/ */
public enum EOverridePolicy { public enum EOverridePolicy {
OVERRIDE_ALL, OVERRIDE_ALL,
MAIN_KEY_ONLY, MAIN_KEY_ONLY,
MAIN_AND_FALLBACK, MAIN_AND_FALLBACK,
FALLBACK_ONLY, FALLBACK_ONLY,
OVERRIDE_NONE OVERRIDE_NONE
} }

View File

@@ -1,385 +1,385 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.ContextParser; import io.dico.dicore.command.parameter.ContextParser;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.ParameterList; import io.dico.dicore.command.parameter.ParameterList;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.*; import java.util.*;
/** /**
* The context of execution. * The context of execution.
* <p> * <p>
* This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values. * This class is responsible for the control flow of parameter parsing, as well as caching and providing the parsed parameter values.
* It is also responsible for keeping track of the parameter to complete in the case of a tab completion. * It is also responsible for keeping track of the parameter to complete in the case of a tab completion.
*/ */
public class ExecutionContext { public class ExecutionContext {
// Sender of the command // Sender of the command
private final CommandSender sender; private final CommandSender sender;
// Address while parsing parameters with ContextParser // Address while parsing parameters with ContextParser
private ICommandAddress address; private ICommandAddress address;
// Command to execute // Command to execute
private Command command; private Command command;
// if this flag is set, this execution is only for completion purposes. // if this flag is set, this execution is only for completion purposes.
private boolean tabComplete; private boolean tabComplete;
private final ArgumentBuffer buffer; private final ArgumentBuffer buffer;
// private ArgumentBuffer processedBuffer; // private ArgumentBuffer processedBuffer;
// caches the buffer's cursor before parsing. This is needed to provide the original input of the player. // caches the buffer's cursor before parsing. This is needed to provide the original input of the player.
private int cursorStart; private int cursorStart;
// when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException. // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException.
//private boolean attemptedToParse; //private boolean attemptedToParse;
// The parsed parameter values, mapped by parameter name. // The parsed parameter values, mapped by parameter name.
// This also includes default values. All parameters from the parameter list are present if parsing was successful. // This also includes default values. All parameters from the parameter list are present if parsing was successful.
private Map<String, Object> parameterValueMap = new HashMap<>(); private Map<String, Object> parameterValueMap = new HashMap<>();
// this set contains the names of the parameters that were present in the command, and not given a default value. // this set contains the names of the parameters that were present in the command, and not given a default value.
private Set<String> parsedParameters = new HashSet<>(); private Set<String> parsedParameters = new HashSet<>();
// these fields store information required to provide completions. // these fields store information required to provide completions.
// the parameter to complete is the parameter that threw an exception when it was parsing. // the parameter to complete is the parameter that threw an exception when it was parsing.
// the exception's message was discarded because it is a completion. // the exception's message was discarded because it is a completion.
private Parameter<?, ?> parameterToComplete; private Parameter<?, ?> parameterToComplete;
// this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested. // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested.
private int parameterToCompleteCursor = -1; private int parameterToCompleteCursor = -1;
// if this flag is set, any messages sent through the sendMessage methods are discarded. // if this flag is set, any messages sent through the sendMessage methods are discarded.
private boolean muted; private boolean muted;
public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) { public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) {
this.sender = Objects.requireNonNull(sender); this.sender = Objects.requireNonNull(sender);
this.buffer = Objects.requireNonNull(buffer); this.buffer = Objects.requireNonNull(buffer);
this.muted = tabComplete; this.muted = tabComplete;
this.tabComplete = tabComplete; this.tabComplete = tabComplete;
// If its tab completing, keep the empty element that might be at the end of the buffer // If its tab completing, keep the empty element that might be at the end of the buffer
// due to a space at the end of the command. // due to a space at the end of the command.
// This allows the parser to correctly identify the parameter to be completed in this case. // This allows the parser to correctly identify the parameter to be completed in this case.
if (!tabComplete) { if (!tabComplete) {
buffer.dropTrailingEmptyElements(); buffer.dropTrailingEmptyElements();
} }
} }
/** /**
* Construct an execution context that is ready to parse the parameter values. * Construct an execution context that is ready to parse the parameter values.
* *
* @param sender the sender * @param sender the sender
* @param address the address * @param address the address
* @param command the command * @param command the command
* @param buffer the arguments * @param buffer the arguments
* @param tabComplete true if this execution is a tab-completion * @param tabComplete true if this execution is a tab-completion
*/ */
public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) { public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) {
this(sender, buffer, tabComplete); this(sender, buffer, tabComplete);
setAddress(address); setAddress(address);
setCommand(command); setCommand(command);
} }
/** /**
* Sender of the command * Sender of the command
* *
* @return the sender of the command * @return the sender of the command
*/ */
public CommandSender getSender() { public CommandSender getSender() {
return sender; return sender;
} }
/** /**
* @return the buffer of arguments * @return the buffer of arguments
*/ */
public ArgumentBuffer getBuffer() { public ArgumentBuffer getBuffer() {
return buffer; return buffer;
} }
/** /**
* Command's address * Command's address
* *
* @return the command's address * @return the command's address
*/ */
public ICommandAddress getAddress() { public ICommandAddress getAddress() {
return address; return address;
} }
/** /**
* Set the address * Set the address
* *
* @param address the new address * @param address the new address
*/ */
public void setAddress(ICommandAddress address) { public void setAddress(ICommandAddress address) {
this.address = address; this.address = address;
} }
/** /**
* The command * The command
* *
* @return the command * @return the command
*/ */
public Command getCommand() { public Command getCommand() {
return command; return command;
} }
/** /**
* Set the command * Set the command
* *
* @param command the new command * @param command the new command
*/ */
public void setCommand(Command command) { public void setCommand(Command command) {
this.command = command; this.command = command;
} }
/** /**
* @return true if this context is for a tab completion. * @return true if this context is for a tab completion.
*/ */
public boolean isTabComplete() { public boolean isTabComplete() {
return tabComplete; return tabComplete;
} }
/** /**
* @return true if this context is muted. * @return true if this context is muted.
*/ */
public boolean isMuted() { public boolean isMuted() {
return muted; return muted;
} }
/** /**
* Parse parameters from the given parameter list, * Parse parameters from the given parameter list,
* adding their values to the cache of this context. * adding their values to the cache of this context.
* *
* @param parameterList the parameterList * @param parameterList the parameterList
* @throws CommandException if the arguments are not valid * @throws CommandException if the arguments are not valid
*/ */
public void parse(ParameterList parameterList) throws CommandException { public void parse(ParameterList parameterList) throws CommandException {
cursorStart = buffer.getCursor(); cursorStart = buffer.getCursor();
ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters); ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters);
try { try {
parser.parse(); parser.parse();
} finally { } finally {
if (tabComplete) { if (tabComplete) {
parameterToComplete = parser.getCompletionTarget(); parameterToComplete = parser.getCompletionTarget();
parameterToCompleteCursor = parser.getCompletionCursor(); parameterToCompleteCursor = parser.getCompletionCursor();
} }
} }
} }
/** /**
* The command's parameter definition. * The command's parameter definition.
* *
* @return the parameter list * @return the parameter list
*/ */
@Deprecated @Deprecated
public ParameterList getParameterList() { public ParameterList getParameterList() {
return null;//command.getParameterList(); return null;//command.getParameterList();
} }
/** /**
* Get the buffer as it was before preprocessing the arguments. * Get the buffer as it was before preprocessing the arguments.
* *
* @return the original buffer * @return the original buffer
*/ */
@Deprecated @Deprecated
public ArgumentBuffer getOriginalBuffer() { public ArgumentBuffer getOriginalBuffer() {
return buffer; return buffer;
} }
/** /**
* The arguments * The arguments
* *
* @return the argument buffer * @return the argument buffer
*/ */
@Deprecated @Deprecated
public ArgumentBuffer getProcessedBuffer() { public ArgumentBuffer getProcessedBuffer() {
return buffer; return buffer;
} }
/** /**
* The cursor start, in other words, the buffer's cursor before parameters were parsed. * The cursor start, in other words, the buffer's cursor before parameters were parsed.
* *
* @return the cursor start * @return the cursor start
*/ */
public int getCursorStart() { public int getCursorStart() {
return cursorStart; return cursorStart;
} }
/** /**
* The original arguments. * The original arguments.
* *
* @return original arguments. * @return original arguments.
*/ */
public String[] getOriginal() { public String[] getOriginal() {
return buffer.getArrayFromIndex(cursorStart); return buffer.getArrayFromIndex(cursorStart);
} }
/** /**
* The path used to access this address. * The path used to access this address.
* *
* @return the path used to access this address. * @return the path used to access this address.
*/ */
public String[] getRoute() { public String[] getRoute() {
return Arrays.copyOf(buffer.toArray(), address.getDepth()); return Arrays.copyOf(buffer.toArray(), address.getDepth());
} }
public Formatting getFormat(EMessageType type) { public Formatting getFormat(EMessageType type) {
return address.getChatHandler().getChatFormatForType(type); return address.getChatHandler().getChatFormatForType(type);
} }
/** /**
* The full command as cached by the buffer. Might be incomplete depending on how it was dispatched. * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched.
* *
* @return the full command * @return the full command
*/ */
public String getRawInput() { public String getRawInput() {
return buffer.getRawInput(); return buffer.getRawInput();
} }
/** /**
* Get the value of the parameter with the given name * Get the value of the parameter with the given name
* *
* @param name the parameter's name * @param name the parameter's name
* @param <T> expected type * @param <T> expected type
* @return the parsed value or the default value * @return the parsed value or the default value
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T get(String name) { public <T> T get(String name) {
if (!parameterValueMap.containsKey(name)) { if (!parameterValueMap.containsKey(name)) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
try { try {
return (T) parameterValueMap.get(name); return (T) parameterValueMap.get(name);
} catch (ClassCastException ex) { } catch (ClassCastException ex) {
throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex); throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex);
} }
} }
/** /**
* Get the value of the flag with the given name * Get the value of the flag with the given name
* *
* @param flag the flag's name, without preceding "-" * @param flag the flag's name, without preceding "-"
* @param <T> expected type * @param <T> expected type
* @return the parsed value or the default value * @return the parsed value or the default value
*/ */
public <T> T getFlag(String flag) { public <T> T getFlag(String flag) {
return get("-" + flag); return get("-" + flag);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Deprecated @Deprecated
public <T> T get(int index) { public <T> T get(int index) {
return null;//get(getParameterList().getIndexedParameterName(index)); return null;//get(getParameterList().getIndexedParameterName(index));
} }
/** /**
* Checks if the parameter by the name was provided in the command's arguments. * Checks if the parameter by the name was provided in the command's arguments.
* *
* @param name the parameter name * @param name the parameter name
* @return true if it was provided * @return true if it was provided
*/ */
public boolean isProvided(String name) { public boolean isProvided(String name) {
return parsedParameters.contains(name); return parsedParameters.contains(name);
} }
/** /**
* Checks if the parameter by the index was provided in the command's arguments. * Checks if the parameter by the index was provided in the command's arguments.
* *
* @param index the parameter index * @param index the parameter index
* @return true if it was provided * @return true if it was provided
*/ */
@Deprecated @Deprecated
public boolean isProvided(int index) { public boolean isProvided(int index) {
return false;//isProvided(getParameterList().getIndexedParameterName(index)); return false;//isProvided(getParameterList().getIndexedParameterName(index));
} }
/** /**
* The parameter to complete. * The parameter to complete.
* This parameter is requested suggestions * This parameter is requested suggestions
* *
* @return the parameter to complete. * @return the parameter to complete.
*/ */
public Parameter<?, ?> getParameterToComplete() { public Parameter<?, ?> getParameterToComplete() {
return parameterToComplete; return parameterToComplete;
} }
/** /**
* Get suggested completions. * Get suggested completions.
* *
* @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way. * @param location The location as passed to {link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}, or null if requested in another way.
* @return completions. * @return completions.
*/ */
public List<String> getSuggestedCompletions(Location location) { public List<String> getSuggestedCompletions(Location location) {
if (parameterToComplete != null) { if (parameterToComplete != null) {
return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor)); return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor));
} }
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (String name : parameterValueMap.keySet()) { for (String name : parameterValueMap.keySet()) {
if (name.startsWith("-") && !parsedParameters.contains(name)) { if (name.startsWith("-") && !parsedParameters.contains(name)) {
result.add(name); result.add(name);
} }
} }
return result; return result;
} }
/* /*
Chat handling Chat handling
*/ */
public void sendMessage(String message) { public void sendMessage(String message) {
sendMessage(true, message); sendMessage(true, message);
} }
public void sendMessage(EMessageType messageType, String message) { public void sendMessage(EMessageType messageType, String message) {
sendMessage(messageType, true, message); sendMessage(messageType, true, message);
} }
public void sendMessage(boolean translateColours, String message) { public void sendMessage(boolean translateColours, String message) {
sendMessage(EMessageType.NEUTRAL, translateColours, message); sendMessage(EMessageType.NEUTRAL, translateColours, message);
} }
public void sendMessage(EMessageType messageType, boolean translateColours, String message) { public void sendMessage(EMessageType messageType, boolean translateColours, String message) {
if (!muted) { if (!muted) {
if (translateColours) { if (translateColours) {
message = Formatting.translateChars('&', message); message = Formatting.translateChars('&', message);
} }
address.getChatHandler().sendMessage(this, messageType, message); address.getChatHandler().sendMessage(this, messageType, message);
} }
} }
public void sendMessage(String messageFormat, Object... args) { public void sendMessage(String messageFormat, Object... args) {
sendMessage(true, messageFormat, args); sendMessage(true, messageFormat, args);
} }
public void sendMessage(EMessageType messageType, String messageFormat, Object... args) { public void sendMessage(EMessageType messageType, String messageFormat, Object... args) {
sendMessage(messageType, true, messageFormat, args); sendMessage(messageType, true, messageFormat, args);
} }
public void sendMessage(boolean translateColours, String messageFormat, Object... args) { public void sendMessage(boolean translateColours, String messageFormat, Object... args) {
sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args); sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args);
} }
public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) { public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) {
sendMessage(messageType, translateColours, String.format(messageFormat, args)); sendMessage(messageType, translateColours, String.format(messageFormat, args));
} }
public void sendHelpMessage(int page) { public void sendHelpMessage(int page) {
if (!muted) { if (!muted) {
address.getChatHandler().sendHelpMessage(sender, this, address, page); address.getChatHandler().sendHelpMessage(sender, this, address, page);
} }
} }
public void sendSyntaxMessage() { public void sendSyntaxMessage() {
if (!muted) { if (!muted) {
address.getChatHandler().sendSyntaxMessage(sender, this, address); address.getChatHandler().sendSyntaxMessage(sender, this, address);
} }
} }
} }

View File

@@ -1,64 +1,64 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.parameter.IArgumentPreProcessor; import io.dico.dicore.command.parameter.IArgumentPreProcessor;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.type.ParameterType; import io.dico.dicore.command.parameter.type.ParameterType;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command { public abstract class ExtendedCommand<T extends ExtendedCommand<T>> extends Command {
protected boolean modifiable; protected boolean modifiable;
public ExtendedCommand() { public ExtendedCommand() {
this(true); this(true);
} }
public ExtendedCommand(boolean modifiable) { public ExtendedCommand(boolean modifiable) {
this.modifiable = modifiable; this.modifiable = modifiable;
} }
protected T newModifiableInstance() { protected T newModifiableInstance() {
return (T) this; return (T) this;
} }
@Override @Override
public T addParameter(Parameter<?, ?> parameter) { public T addParameter(Parameter<?, ?> parameter) {
return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter); return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter);
} }
@Override @Override
public T addContextFilter(IContextFilter contextFilter) { public T addContextFilter(IContextFilter contextFilter) {
return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter); return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter);
} }
@Override @Override
public T removeContextFilter(IContextFilter contextFilter) { public T removeContextFilter(IContextFilter contextFilter) {
return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter); return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter);
} }
@Override @Override
public T requiredParameters(int requiredParameters) { public T requiredParameters(int requiredParameters) {
return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters); return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters);
} }
@Override @Override
public T repeatFinalParameter() { public T repeatFinalParameter() {
return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter(); return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter();
} }
@Override @Override
public T setDescription(String... description) { public T setDescription(String... description) {
return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description); return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description);
} }
@Override @Override
public T setShortDescription(String shortDescription) { public T setShortDescription(String shortDescription) {
return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription); return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription);
} }
/* /*
@Override @Override
public T preprocessArguments(IArgumentPreProcessor processor) { public T preprocessArguments(IArgumentPreProcessor processor) {
return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor); return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor);
}*/ }*/
} }

View File

@@ -1,205 +1,205 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.chat.IChatHandler; import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.ParameterList; import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.predef.PredefinedCommand; import io.dico.dicore.command.predef.PredefinedCommand;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Interface for an address of a command. * Interface for an address of a command.
* <p> * <p>
* The address holds what the name and aliases of a command are. * The address holds what the name and aliases of a command are.
* The address also (optionally) holds a reference to a {@link Command} * The address also (optionally) holds a reference to a {@link Command}
* <p> * <p>
* One instance of {@link Command} can be held by multiple addresses, * One instance of {@link Command} can be held by multiple addresses,
* because the address decides what the command's name and aliases are. * because the address decides what the command's name and aliases are.
* <p> * <p>
* The address holds children by key in a map. This map's keys include aliases for its children. * The address holds children by key in a map. This map's keys include aliases for its children.
* This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found * This creates a tree of addresses. If a command is dispatches, the tree is traversed untill a command is found
* and no children deeper down match the command (there are exceptions to the later as defined by * and no children deeper down match the command (there are exceptions to the later as defined by
* {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
* and {@link Command#isVisibleTo(CommandSender)} * and {@link Command#isVisibleTo(CommandSender)}
*/ */
public interface ICommandAddress { public interface ICommandAddress {
/** /**
* @return true if this address has a parent. * @return true if this address has a parent.
*/ */
boolean hasParent(); boolean hasParent();
/** /**
* Get the parent of this address * Get the parent of this address
* *
* @return the parent of this address, or null if none exists. * @return the parent of this address, or null if none exists.
*/ */
ICommandAddress getParent(); ICommandAddress getParent();
/** /**
* @return true if this address has a command. * @return true if this address has a command.
*/ */
boolean hasCommand(); boolean hasCommand();
/** /**
* @return true if this address has a command that is not an instance of {@link PredefinedCommand} * @return true if this address has a command that is not an instance of {@link PredefinedCommand}
*/ */
boolean hasUserDeclaredCommand(); boolean hasUserDeclaredCommand();
/** /**
* @return Get the command of this address, or null if none exists. * @return Get the command of this address, or null if none exists.
*/ */
Command getCommand(); Command getCommand();
/** /**
* @return true if this address is an instance of {@link RootCommandAddress} * @return true if this address is an instance of {@link RootCommandAddress}
*/ */
boolean isRoot(); boolean isRoot();
/** /**
* @return the root address of the tree which this address resides in. * @return the root address of the tree which this address resides in.
*/ */
ICommandAddress getRoot(); ICommandAddress getRoot();
/** /**
* A list of the names of this address, at the current level. * A list of the names of this address, at the current level.
* The first entry is the main key, the subsequent ones are aliases. * The first entry is the main key, the subsequent ones are aliases.
* <p> * <p>
* Untill an address is assigned a parent, this list is mutable. * Untill an address is assigned a parent, this list is mutable.
* <p> * <p>
* If {@link #isRoot()}, this returns an immutable, empty list. * If {@link #isRoot()}, this returns an immutable, empty list.
* *
* @return the list of names. * @return the list of names.
*/ */
List<String> getNames(); List<String> getNames();
/** /**
* A list of the aliases of this address. That is, {@link #getNames()} * A list of the aliases of this address. That is, {@link #getNames()}
* without the first entry. * without the first entry.
* *
* @return a list of aliases * @return a list of aliases
*/ */
List<String> getAliases(); List<String> getAliases();
/** /**
* @return The first element of {@link #getNames()} * @return The first element of {@link #getNames()}
*/ */
String getMainKey(); String getMainKey();
/** /**
* Get the address of this command. * Get the address of this command.
* That is, the main keys of all commands leading up to this address, and this address itself, separated by a space. * That is, the main keys of all commands leading up to this address, and this address itself, separated by a space.
* In other words, the command without the / that is required to target the command at this address. * In other words, the command without the / that is required to target the command at this address.
* *
* @return the address of this command. * @return the address of this command.
*/ */
String getAddress(); String getAddress();
/** /**
* Get the amount of addresses that separate this address from the root of the tree, + 1. * Get the amount of addresses that separate this address from the root of the tree, + 1.
* The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1. * The root of the tree has a depth of 0. Each subsequent child has its depth incremented by 1.
* *
* @return The depth of this address * @return The depth of this address
*/ */
int getDepth(); int getDepth();
/** /**
* @return true if the depth of this address is larger than the argument. * @return true if the depth of this address is larger than the argument.
*/ */
boolean isDepthLargerThan(int depth); boolean isDepthLargerThan(int depth);
/** /**
* @return true if this address has any children. * @return true if this address has any children.
*/ */
boolean hasChildren(); boolean hasChildren();
/** /**
* @return total number of children, not considering any aliases * @return total number of children, not considering any aliases
*/ */
int getNumberOfRealChildren(); int getNumberOfRealChildren();
/** /**
* Get an unmodifiable view of all main keys of the children of this address. * Get an unmodifiable view of all main keys of the children of this address.
* *
* @return the main keys * @return the main keys
*/ */
Collection<String> getChildrenMainKeys(); Collection<String> getChildrenMainKeys();
/** /**
* Get an unmodifiable view of the children of this address. * Get an unmodifiable view of the children of this address.
* Values might be duplicated for aliases. * Values might be duplicated for aliases.
* *
* <p> * <p>
* To iterate children without duplicates, you can do something like this: * To iterate children without duplicates, you can do something like this:
* <pre>{@code * <pre>{@code
* for (String key : address.getChildrenMainKeys()) { * for (String key : address.getChildrenMainKeys()) {
* ICommandAddress child = address.getChild(key); * ICommandAddress child = address.getChild(key);
* // do stuff with child * // do stuff with child
* } * }
* }</pre> * }</pre>
* </p> * </p>
* *
* @return the children of this address. * @return the children of this address.
*/ */
Map<String, ? extends ICommandAddress> getChildren(); Map<String, ? extends ICommandAddress> getChildren();
/** /**
* Query for a child at the given key. * Query for a child at the given key.
* *
* @param key the key. The name or alias of a command. * @param key the key. The name or alias of a command.
* @return the child, or null if it's not found * @return the child, or null if it's not found
*/ */
ICommandAddress getChild(String key); ICommandAddress getChild(String key);
/** /**
* Query for a child using the given buffer, with the given context for reference. * Query for a child using the given buffer, with the given context for reference.
* Can be used to override behaviour of the address tree. * Can be used to override behaviour of the address tree.
* <p> * <p>
* The default implementation is as follows: * The default implementation is as follows:
* <pre>{@code * <pre>{@code
* return buffer.hasNext() ? getChild(buffer.next()) : null; * return buffer.hasNext() ? getChild(buffer.next()) : null;
* }</pre> * }</pre>
* *
* @param context context of a command being executed * @param context context of a command being executed
* @param buffer the buffer. The name or alias of a command. * @param buffer the buffer. The name or alias of a command.
* @return the child, or null if it's not found, altered freely by the implementation * @return the child, or null if it's not found, altered freely by the implementation
*/ */
ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
/** /**
* Get the command dispatcher for this tree * Get the command dispatcher for this tree
* *
* @return the command dispatcher * @return the command dispatcher
*/ */
ICommandDispatcher getDispatcherForTree(); ICommandDispatcher getDispatcherForTree();
/** /**
* @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler. * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler.
*/ */
IChatHandler getChatHandler(); IChatHandler getChatHandler();
/** /**
* Returns if the command attached to this address should be treated as trailing. * Returns if the command attached to this address should be treated as trailing.
* A trailing command is executed whenever the address is scanned for children. * A trailing command is executed whenever the address is scanned for children.
* Its parameters are parsed and added to the context. * Its parameters are parsed and added to the context.
* *
* @return true if the command attached to this address should be treated as trailing. * @return true if the command attached to this address should be treated as trailing.
*/ */
boolean isCommandTrailing(); boolean isCommandTrailing();
static ICommandAddress newChild() { static ICommandAddress newChild() {
return new ChildCommandAddress(); return new ChildCommandAddress();
} }
static ICommandAddress newChild(Command command) { static ICommandAddress newChild(Command command) {
return new ChildCommandAddress(command); return new ChildCommandAddress(command);
} }
static ICommandAddress newRoot() { static ICommandAddress newRoot() {
return new RootCommandAddress(); return new RootCommandAddress();
} }
} }

View File

@@ -1,146 +1,146 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.registration.CommandMap; import io.dico.dicore.command.registration.CommandMap;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public interface ICommandDispatcher { public interface ICommandDispatcher {
/** /**
* Get a potentially indirect child of the root of this dispatcher * Get a potentially indirect child of the root of this dispatcher
* *
* @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored. * @param buffer the argument buffer with the subsequent keys to traverse. Any keys beyond the first that isn't found are ignored.
* @return the child, or this same instance of no child is found. * @return the child, or this same instance of no child is found.
*/ */
ICommandAddress getDeepChild(ArgumentBuffer buffer); ICommandAddress getDeepChild(ArgumentBuffer buffer);
/** /**
* Similar to {@link #getDeepChild(ArgumentBuffer)}, * Similar to {@link #getDeepChild(ArgumentBuffer)},
* but this method incorporates checks on the command of traversed children: * but this method incorporates checks on the command of traversed children:
* {@link Command#isVisibleTo(CommandSender)} * {@link Command#isVisibleTo(CommandSender)}
* and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
* <p> * <p>
* The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command. * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
* *
* @param sender the sender of the command * @param sender the sender of the command
* @param buffer the command itself as a buffer. * @param buffer the command itself as a buffer.
* @return the address that is the target of the command. * @return the address that is the target of the command.
*/ */
@Deprecated @Deprecated
ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer); ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer);
/** /**
* Similar to {@link #getDeepChild(ArgumentBuffer)}, * Similar to {@link #getDeepChild(ArgumentBuffer)},
* but this method incorporates checks on the command of traversed children: * but this method incorporates checks on the command of traversed children:
* {@link Command#isVisibleTo(CommandSender)} * {@link Command#isVisibleTo(CommandSender)}
* and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)}
* <p> * <p>
* The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command. * The target of a command is never null, however, the same instance might be returned, and the returned address might not hold a command.
* *
* @param context the context of the command. The context must not have its address set. * @param context the context of the command. The context must not have its address set.
* @param buffer the command itself as a buffer. * @param buffer the command itself as a buffer.
* @return the address that is the target of the command. * @return the address that is the target of the command.
*/ */
ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException;
/** /**
* dispatch the command * dispatch the command
* *
* @param sender the sender * @param sender the sender
* @param command the command * @param command the command
* @return true if a command has executed * @return true if a command has executed
*/ */
boolean dispatchCommand(CommandSender sender, String[] command); boolean dispatchCommand(CommandSender sender, String[] command);
/** /**
* dispatch the command * dispatch the command
* *
* @param sender the sender * @param sender the sender
* @param usedLabel the label (word after the /) * @param usedLabel the label (word after the /)
* @param args the arguments * @param args the arguments
* @return true if a command has executed * @return true if a command has executed
*/ */
boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args); boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args);
/** /**
* dispatch the command * dispatch the command
* *
* @param sender the sender * @param sender the sender
* @param buffer the command * @param buffer the command
* @return true if a command has executed * @return true if a command has executed
*/ */
boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer); boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer);
/** /**
* suggest tab completions * suggest tab completions
* *
* @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* args must be sanitized such that it contains no empty elements, particularly at the last index. * args must be sanitized such that it contains no empty elements, particularly at the last index.
* @return tab completions * @return tab completions
*/ */
List<String> getTabCompletions(CommandSender sender, Location location, String[] args); List<String> getTabCompletions(CommandSender sender, Location location, String[] args);
/** /**
* suggest tab completions * suggest tab completions
* *
* @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param usedLabel the label as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param args the arguments as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @return tab completions * @return tab completions
*/ */
List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args); List<String> getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args);
/** /**
* suggest tab completions * suggest tab completions
* *
* @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param sender the sender as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)} * @param location the location as passed to {@link org.bukkit.command.Command#tabComplete(CommandSender, String, String[], Location)}
* @param buffer the arguments as a buffer * @param buffer the arguments as a buffer
* @return tab completions * @return tab completions
*/ */
List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer); List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer);
/** /**
* Register this dispatcher's commands to the command map * Register this dispatcher's commands to the command map
* *
* @throws UnsupportedOperationException if this dispatcher is not the root of the tree * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
*/ */
default void registerToCommandMap() { default void registerToCommandMap() {
registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL); registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL);
} }
/** /**
* Register this dispatcher's commands to the command map * Register this dispatcher's commands to the command map
* *
* @param fallbackPrefix the fallback prefix to use, null if none * @param fallbackPrefix the fallback prefix to use, null if none
* @param overridePolicy the override policy * @param overridePolicy the override policy
* @throws UnsupportedOperationException if this dispatcher is not the root of the tree * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
*/ */
default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) { default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) {
registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy); registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy);
} }
/** /**
* Register this dispatcher's commands to the command map * Register this dispatcher's commands to the command map
* *
* @param fallbackPrefix the fallback prefix to use, null if none * @param fallbackPrefix the fallback prefix to use, null if none
* @param map the command map * @param map the command map
* @param overridePolicy the override policy * @param overridePolicy the override policy
* @throws UnsupportedOperationException if this dispatcher is not the root of the tree * @throws UnsupportedOperationException if this dispatcher is not the root of the tree
*/ */
void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy); void registerToCommandMap(String fallbackPrefix, Map<String, org.bukkit.command.Command> map, EOverridePolicy overridePolicy);
default void unregisterFromCommandMap() { default void unregisterFromCommandMap() {
unregisterFromCommandMap(CommandMap.getCommandMap()); unregisterFromCommandMap(CommandMap.getCommandMap());
} }
void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map); void unregisterFromCommandMap(Map<String, org.bukkit.command.Command> map);
} }

View File

@@ -1,201 +1,201 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer; import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer;
import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public interface IContextFilter extends Comparable<IContextFilter> { public interface IContextFilter extends Comparable<IContextFilter> {
/** /**
* Filter the given context by this filter's criteria. * Filter the given context by this filter's criteria.
* If the context does not match the criteria, an exception is thrown describing the problem. * If the context does not match the criteria, an exception is thrown describing the problem.
* *
* @param context the context to match * @param context the context to match
* @throws CommandException if it doesn't match * @throws CommandException if it doesn't match
*/ */
void filterContext(ExecutionContext context) throws CommandException; void filterContext(ExecutionContext context) throws CommandException;
/** /**
* Filter an execution context for a direct or indirect sub command of the command that registered this filter. * Filter an execution context for a direct or indirect sub command of the command that registered this filter.
* *
* @param subContext the context for the execution * @param subContext the context for the execution
* @param path the path traversed from the command that registered this filter to the executed command * @param path the path traversed from the command that registered this filter to the executed command
*/ */
default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
filterContext(subContext); filterContext(subContext);
} }
/** /**
* Get the priority of this context filter. * Get the priority of this context filter.
* The priorities determine the order in which a command's context filters are executed. * The priorities determine the order in which a command's context filters are executed.
* *
* @return the priority * @return the priority
*/ */
Priority getPriority(); Priority getPriority();
default boolean allowsContext(ExecutionContext context) { default boolean allowsContext(ExecutionContext context) {
try { try {
filterContext(context); filterContext(context);
return true; return true;
} catch (CommandException ex) { } catch (CommandException ex) {
return false; return false;
} }
} }
/** /**
* Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()} * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()}
* *
* @param o compared filter * @param o compared filter
* @return comparison value * @return comparison value
*/ */
@Override @Override
default int compareTo(@NotNull IContextFilter o) { default int compareTo(@NotNull IContextFilter o) {
return getPriority().compareTo(o.getPriority()); return getPriority().compareTo(o.getPriority());
} }
/* /*
default boolean isInheritable() { default boolean isInheritable() {
return false; return false;
} }
default IContextFilter inherit(String... components) { default IContextFilter inherit(String... components) {
if (!isInheritable()) { if (!isInheritable()) {
throw new IllegalStateException("This IContextFilter cannot be inherited"); throw new IllegalStateException("This IContextFilter cannot be inherited");
} }
return this; return this;
}*/ }*/
/** /**
* IContextFilter priorities. Executes from top to bottom. * IContextFilter priorities. Executes from top to bottom.
*/ */
enum Priority { enum Priority {
/** /**
* This priority should have checks on the sender type. * This priority should have checks on the sender type.
* Any filters on this priority are tested before permissions are. * Any filters on this priority are tested before permissions are.
* This is the highest priority. * This is the highest priority.
*/ */
VERY_EARLY, // sender type check VERY_EARLY, // sender type check
/** /**
* This priority is specific to permissions. * This priority is specific to permissions.
*/ */
PERMISSION, PERMISSION,
/** /**
* Early priority. Post permissions, pre parameter-parsing. * Early priority. Post permissions, pre parameter-parsing.
*/ */
EARLY, EARLY,
/** /**
* Normal priority. Post permissions, pre parameter-parsing. * Normal priority. Post permissions, pre parameter-parsing.
*/ */
NORMAL, NORMAL,
/** /**
* Late priority. Post permissions, pre parameter-parsing. * Late priority. Post permissions, pre parameter-parsing.
*/ */
LATE, LATE,
/** /**
* Very late priority. Post permissions, pre parameter-parsing. * Very late priority. Post permissions, pre parameter-parsing.
*/ */
VERY_LATE, VERY_LATE,
/** /**
* Post parameters priority. Post permissions, post parameter-parsing. * Post parameters priority. Post permissions, post parameter-parsing.
* This is the lowest priority. * This is the lowest priority.
*/ */
POST_PARAMETERS; POST_PARAMETERS;
private IContextFilter inheritor; private IContextFilter inheritor;
/** /**
* Get the context filter that inherits context filters from the parent of the same priority. * Get the context filter that inherits context filters from the parent of the same priority.
* If this filter is also present at the parent, it will do the same for the parent's parent, and so on. * If this filter is also present at the parent, it will do the same for the parent's parent, and so on.
* *
* @return the inheritor * @return the inheritor
*/ */
public IContextFilter getInheritor() { public IContextFilter getInheritor() {
if (inheritor == null) { if (inheritor == null) {
inheritor = InheritingContextFilter.inheritingPriority(this); inheritor = InheritingContextFilter.inheritingPriority(this);
} }
return inheritor; return inheritor;
} }
} }
/** /**
* Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command. * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command.
*/ */
IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer); IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer);
/** /**
* Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command. * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command.
*/ */
IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole); IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole);
/** /**
* This filter is not working as intended. * This filter is not working as intended.
* <p> * <p>
* There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission. * There is supposed to be a permission filter that takes a base, and appends the command's address to the base, and checks that permission.
*/ */
IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor(); IContextFilter INHERIT_PERMISSIONS = Priority.PERMISSION.getInheritor();
static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) { static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable<? extends CommandException> runnable) {
return new IContextFilter() { return new IContextFilter() {
@Override @Override
public void filterContext(ExecutionContext context) throws CommandException { public void filterContext(ExecutionContext context) throws CommandException {
runnable.checkedRun(); runnable.checkedRun();
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return priority; return priority;
} }
}; };
} }
static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) { static IContextFilter filterSender(Priority priority, CheckedConsumer<? super CommandSender, ? extends CommandException> consumer) {
return new IContextFilter() { return new IContextFilter() {
@Override @Override
public void filterContext(ExecutionContext context) throws CommandException { public void filterContext(ExecutionContext context) throws CommandException {
consumer.checkedAccept(context.getSender()); consumer.checkedAccept(context.getSender());
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return priority; return priority;
} }
}; };
} }
static IContextFilter permission(String permission) { static IContextFilter permission(String permission) {
return new PermissionContextFilter(permission); return new PermissionContextFilter(permission);
} }
static IContextFilter permission(String permission, String failMessage) { static IContextFilter permission(String permission, String failMessage) {
return new PermissionContextFilter(permission, failMessage); return new PermissionContextFilter(permission, failMessage);
} }
static IContextFilter inheritablePermission(String permission) { static IContextFilter inheritablePermission(String permission) {
return new PermissionContextFilter(permission, true); return new PermissionContextFilter(permission, true);
} }
/** /**
* Produce an inheritable permission context filter. * Produce an inheritable permission context filter.
* A permission component is an element in {@code permission.split("\\.")} * A permission component is an element in {@code permission.split("\\.")}
* *
* @param permission The permission that is required for the command that this is directly assigned to * @param permission The permission that is required for the command that this is directly assigned to
* @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end". * @param componentInsertionIndex the index where any sub-components are inserted. -1 for "at the end".
* @param failMessage the message to send if the permission is not met * @param failMessage the message to send if the permission is not met
* @return the context filter * @return the context filter
* @throws IllegalArgumentException if componentInsertionIndex is out of range * @throws IllegalArgumentException if componentInsertionIndex is out of range
*/ */
static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) { static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) {
return new PermissionContextFilter(permission, componentInsertionIndex, failMessage); return new PermissionContextFilter(permission, componentInsertionIndex, failMessage);
} }
} }

View File

@@ -1,64 +1,64 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import java.util.List; import java.util.List;
public abstract class InheritingContextFilter implements IContextFilter { public abstract class InheritingContextFilter implements IContextFilter {
private static final String[] emptyStringArray = new String[0]; private static final String[] emptyStringArray = new String[0];
private static String[] addParent(String[] path, String parent) { private static String[] addParent(String[] path, String parent) {
String[] out = new String[path.length + 1]; String[] out = new String[path.length + 1];
System.arraycopy(path, 0, out, 1, path.length); System.arraycopy(path, 0, out, 1, path.length);
out[0] = parent; out[0] = parent;
return out; return out;
} }
protected abstract boolean isInherited(IContextFilter filter); protected abstract boolean isInherited(IContextFilter filter);
@Override @Override
public void filterContext(ExecutionContext context) throws CommandException { public void filterContext(ExecutionContext context) throws CommandException {
ICommandAddress address = context.getAddress(); ICommandAddress address = context.getAddress();
String[] traversedPath = emptyStringArray; String[] traversedPath = emptyStringArray;
do { do {
traversedPath = addParent(traversedPath, address.getMainKey()); traversedPath = addParent(traversedPath, address.getMainKey());
address = address.getParent(); address = address.getParent();
if (address != null && address.hasCommand()) { if (address != null && address.hasCommand()) {
boolean doBreak = true; boolean doBreak = true;
Command command = address.getCommand(); Command command = address.getCommand();
List<IContextFilter> contextFilterList = command.getContextFilters(); List<IContextFilter> contextFilterList = command.getContextFilters();
for (IContextFilter filter : contextFilterList) { for (IContextFilter filter : contextFilterList) {
if (isInherited(filter)) { if (isInherited(filter)) {
if (filter == this) { if (filter == this) {
// do the same for next parent // do the same for next parent
// this method is necessary to keep traversedPath information // this method is necessary to keep traversedPath information
doBreak = false; doBreak = false;
} else { } else {
filter.filterSubContext(context, traversedPath); filter.filterSubContext(context, traversedPath);
} }
} }
} }
if (doBreak) { if (doBreak) {
break; break;
} }
} }
} while (address != null); } while (address != null);
} }
static InheritingContextFilter inheritingPriority(Priority priority) { static InheritingContextFilter inheritingPriority(Priority priority) {
return new InheritingContextFilter() { return new InheritingContextFilter() {
@Override @Override
protected boolean isInherited(IContextFilter filter) { protected boolean isInherited(IContextFilter filter) {
return filter.getPriority() == priority; return filter.getPriority() == priority;
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return priority; return priority;
} }
}; };
} }
} }

View File

@@ -1,35 +1,35 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction; import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.BiFunction; import java.util.function.BiFunction;
public class LambdaCommand extends ExtendedCommand<LambdaCommand> { public class LambdaCommand extends ExtendedCommand<LambdaCommand> {
private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor; private CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor;
private BiFunction<CommandSender, ExecutionContext, List<String>> completer; private BiFunction<CommandSender, ExecutionContext, List<String>> completer;
public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) { public LambdaCommand executor(CheckedBiFunction<CommandSender, ExecutionContext, String, CommandException> executor) {
this.executor = Objects.requireNonNull(executor); this.executor = Objects.requireNonNull(executor);
return this; return this;
} }
public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) { public LambdaCommand completer(BiFunction<CommandSender, ExecutionContext, List<String>> completer) {
this.completer = Objects.requireNonNull(completer); this.completer = Objects.requireNonNull(completer);
return this; return this;
} }
@Override @Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException { public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
return executor.checkedApply(sender, context); return executor.checkedApply(sender, context);
} }
@Override @Override
public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) { public List<String> tabComplete(CommandSender sender, ExecutionContext context, Location location) {
return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context); return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context);
} }
} }

View File

@@ -1,312 +1,312 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import io.dico.dicore.command.chat.ChatHandlers; import io.dico.dicore.command.chat.ChatHandlers;
import io.dico.dicore.command.chat.IChatHandler; import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.ParameterList; import io.dico.dicore.command.parameter.ParameterList;
import io.dico.dicore.command.predef.DefaultGroupCommand; import io.dico.dicore.command.predef.DefaultGroupCommand;
import io.dico.dicore.command.predef.HelpCommand; import io.dico.dicore.command.predef.HelpCommand;
import io.dico.dicore.command.predef.PredefinedCommand; import io.dico.dicore.command.predef.PredefinedCommand;
import java.util.*; import java.util.*;
public abstract class ModifiableCommandAddress implements ICommandAddress { public abstract class ModifiableCommandAddress implements ICommandAddress {
Map<String, ChildCommandAddress> children; Map<String, ChildCommandAddress> children;
Collection<String> childrenMainKeys = Collections.emptyList(); Collection<String> childrenMainKeys = Collections.emptyList();
// the chat handler as configured by the programmer // the chat handler as configured by the programmer
IChatHandler chatHandler; IChatHandler chatHandler;
// cache for the algorithm that finds the first chat handler going up the tree // cache for the algorithm that finds the first chat handler going up the tree
transient IChatHandler cachedChatHandlerFallback; transient IChatHandler cachedChatHandlerFallback;
ModifiableCommandAddress helpChild; ModifiableCommandAddress helpChild;
public ModifiableCommandAddress() { public ModifiableCommandAddress() {
this.children = new LinkedHashMap<>(4); this.children = new LinkedHashMap<>(4);
} }
@Override @Override
public boolean hasParent() { public boolean hasParent() {
return getParent() != null; return getParent() != null;
} }
@Override @Override
public boolean hasCommand() { public boolean hasCommand() {
return getCommand() != null; return getCommand() != null;
} }
@Override @Override
public boolean hasUserDeclaredCommand() { public boolean hasUserDeclaredCommand() {
Command command = getCommand(); Command command = getCommand();
return command != null && !(command instanceof PredefinedCommand); return command != null && !(command instanceof PredefinedCommand);
} }
@Override @Override
public Command getCommand() { public Command getCommand() {
return null; return null;
} }
@Override @Override
public boolean isRoot() { public boolean isRoot() {
return false; return false;
} }
@Override @Override
public List<String> getNames() { public List<String> getNames() {
return null; return null;
} }
@Override @Override
public List<String> getAliases() { public List<String> getAliases() {
List<String> names = getNames(); List<String> names = getNames();
if (names == null) { if (names == null) {
return null; return null;
} }
if (names.isEmpty()) { if (names.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
return names.subList(1, names.size()); return names.subList(1, names.size());
} }
@Override @Override
public String getMainKey() { public String getMainKey() {
return null; return null;
} }
public void setCommand(Command command) { public void setCommand(Command command) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public abstract ModifiableCommandAddress getParent(); public abstract ModifiableCommandAddress getParent();
@Override @Override
public RootCommandAddress getRoot() { public RootCommandAddress getRoot() {
ModifiableCommandAddress out = this; ModifiableCommandAddress out = this;
while (out.hasParent()) { while (out.hasParent()) {
out = out.getParent(); out = out.getParent();
} }
return out.isRoot() ? (RootCommandAddress) out : null; return out.isRoot() ? (RootCommandAddress) out : null;
} }
@Override @Override
public int getDepth() { public int getDepth() {
int depth = 0; int depth = 0;
ICommandAddress address = this; ICommandAddress address = this;
while (address.hasParent()) { while (address.hasParent()) {
address = address.getParent(); address = address.getParent();
depth++; depth++;
} }
return depth; return depth;
} }
@Override @Override
public boolean isDepthLargerThan(int value) { public boolean isDepthLargerThan(int value) {
int depth = 0; int depth = 0;
ICommandAddress address = this; ICommandAddress address = this;
do { do {
if (depth > value) { if (depth > value) {
return true; return true;
} }
address = address.getParent(); address = address.getParent();
depth++; depth++;
} while (address != null); } while (address != null);
return false; return false;
} }
@Override @Override
public boolean hasChildren() { public boolean hasChildren() {
return !children.isEmpty(); return !children.isEmpty();
} }
@Override @Override
public int getNumberOfRealChildren() { public int getNumberOfRealChildren() {
return childrenMainKeys.size(); return childrenMainKeys.size();
} }
@Override @Override
public Collection<String> getChildrenMainKeys() { public Collection<String> getChildrenMainKeys() {
return Collections.unmodifiableCollection(childrenMainKeys); return Collections.unmodifiableCollection(childrenMainKeys);
} }
@Override @Override
public Map<String, ? extends ModifiableCommandAddress> getChildren() { public Map<String, ? extends ModifiableCommandAddress> getChildren() {
return Collections.unmodifiableMap(children); return Collections.unmodifiableMap(children);
} }
@Override @Override
public ChildCommandAddress getChild(String key) { public ChildCommandAddress getChild(String key) {
return children.get(key); return children.get(key);
} }
@Override @Override
public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
return buffer.hasNext() ? getChild(buffer.next()) : null; return buffer.hasNext() ? getChild(buffer.next()) : null;
} }
public void addChild(ICommandAddress child) { public void addChild(ICommandAddress child) {
if (!(child instanceof ChildCommandAddress)) { if (!(child instanceof ChildCommandAddress)) {
throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); throw new IllegalArgumentException("Argument must be a ChildCommandAddress");
} }
ChildCommandAddress mChild = (ChildCommandAddress) child; ChildCommandAddress mChild = (ChildCommandAddress) child;
if (mChild.parent != null) { if (mChild.parent != null) {
throw new IllegalArgumentException("Argument already has a parent"); throw new IllegalArgumentException("Argument already has a parent");
} }
if (mChild.names.isEmpty()) { if (mChild.names.isEmpty()) {
throw new IllegalArgumentException("Argument must have names"); throw new IllegalArgumentException("Argument must have names");
} }
Iterator<String> names = mChild.modifiableNamesIterator(); Iterator<String> names = mChild.modifiableNamesIterator();
String mainKey = names.next(); String mainKey = names.next();
if (!childrenMainKeys.contains(mainKey)) { if (!childrenMainKeys.contains(mainKey)) {
if (!(childrenMainKeys instanceof ArrayList)) { if (!(childrenMainKeys instanceof ArrayList)) {
childrenMainKeys = new ArrayList<>(); childrenMainKeys = new ArrayList<>();
} }
childrenMainKeys.add(mainKey); childrenMainKeys.add(mainKey);
} }
children.put(mainKey, mChild); children.put(mainKey, mChild);
while (names.hasNext()) { while (names.hasNext()) {
String name = names.next(); String name = names.next();
if (children.putIfAbsent(name, mChild) != null) { if (children.putIfAbsent(name, mChild) != null) {
names.remove(); names.remove();
} }
} }
mChild.setParent(this); mChild.setParent(this);
if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) { if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) {
helpChild = mChild; helpChild = mChild;
} }
} }
public void removeChildren(boolean removeAliases, String... keys) { public void removeChildren(boolean removeAliases, String... keys) {
if (keys.length == 0) { if (keys.length == 0) {
throw new IllegalArgumentException("keys is empty"); throw new IllegalArgumentException("keys is empty");
} }
for (String key : keys) { for (String key : keys) {
ChildCommandAddress keyTarget = getChild(key); ChildCommandAddress keyTarget = getChild(key);
if (keyTarget == null) { if (keyTarget == null) {
continue; continue;
} }
if (removeAliases) { if (removeAliases) {
Iterator<String> iterator = keyTarget.namesModifiable.iterator(); Iterator<String> iterator = keyTarget.namesModifiable.iterator();
boolean first = true; boolean first = true;
while (iterator.hasNext()) { while (iterator.hasNext()) {
String alias = iterator.next(); String alias = iterator.next();
ChildCommandAddress aliasTarget = getChild(key); ChildCommandAddress aliasTarget = getChild(key);
if (aliasTarget == keyTarget) { if (aliasTarget == keyTarget) {
if (first) { if (first) {
childrenMainKeys.remove(alias); childrenMainKeys.remove(alias);
} }
children.remove(alias); children.remove(alias);
} }
iterator.remove(); iterator.remove();
first = false; first = false;
} }
} else { } else {
if (key.equals(keyTarget.getMainKey())) { if (key.equals(keyTarget.getMainKey())) {
childrenMainKeys.remove(key); childrenMainKeys.remove(key);
} }
children.remove(key); children.remove(key);
keyTarget.namesModifiable.remove(key); keyTarget.namesModifiable.remove(key);
} }
} }
} }
public boolean hasHelpCommand() { public boolean hasHelpCommand() {
return helpChild != null; return helpChild != null;
} }
public ModifiableCommandAddress getHelpCommand() { public ModifiableCommandAddress getHelpCommand() {
return helpChild; return helpChild;
} }
@Override @Override
public IChatHandler getChatHandler() { public IChatHandler getChatHandler() {
if (cachedChatHandlerFallback == null) { if (cachedChatHandlerFallback == null) {
if (chatHandler != null) { if (chatHandler != null) {
cachedChatHandlerFallback = chatHandler; cachedChatHandlerFallback = chatHandler;
} else if (!hasParent()) { } else if (!hasParent()) {
cachedChatHandlerFallback = ChatHandlers.defaultChat(); cachedChatHandlerFallback = ChatHandlers.defaultChat();
} else { } else {
cachedChatHandlerFallback = getParent().getChatHandler(); cachedChatHandlerFallback = getParent().getChatHandler();
} }
} }
return cachedChatHandlerFallback; return cachedChatHandlerFallback;
} }
public void setChatHandler(IChatHandler chatHandler) { public void setChatHandler(IChatHandler chatHandler) {
this.chatHandler = chatHandler; this.chatHandler = chatHandler;
resetChatHandlerCache(new HashSet<>()); resetChatHandlerCache(new HashSet<>());
} }
void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) { void resetChatHandlerCache(Set<ModifiableCommandAddress> dejaVu) {
if (dejaVu.add(this)) { if (dejaVu.add(this)) {
cachedChatHandlerFallback = chatHandler; cachedChatHandlerFallback = chatHandler;
for (ChildCommandAddress address : children.values()) { for (ChildCommandAddress address : children.values()) {
if (address.chatHandler == null) { if (address.chatHandler == null) {
address.resetChatHandlerCache(dejaVu); address.resetChatHandlerCache(dejaVu);
} }
} }
} }
} }
@Override @Override
public ICommandDispatcher getDispatcherForTree() { public ICommandDispatcher getDispatcherForTree() {
return getRoot(); return getRoot();
} }
@Override @Override
public boolean isCommandTrailing() { public boolean isCommandTrailing() {
return false; return false;
} }
public void setCommandTrailing(boolean trailing) { public void setCommandTrailing(boolean trailing) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
void appendDebugInformation(StringBuilder target, String linePrefix, Set<ICommandAddress> seen) { void appendDebugInformation(StringBuilder target, String linePrefix, Set<ICommandAddress> seen) {
target.append('\n').append(linePrefix); target.append('\n').append(linePrefix);
if (!seen.add(this)) { if (!seen.add(this)) {
target.append("<duplicate of address '").append(getAddress()).append("'>"); target.append("<duplicate of address '").append(getAddress()).append("'>");
return; return;
} }
if (this instanceof ChildCommandAddress) { if (this instanceof ChildCommandAddress) {
List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable; List<String> namesModifiable = ((ChildCommandAddress) this).namesModifiable;
if (namesModifiable.isEmpty()) { if (namesModifiable.isEmpty()) {
target.append("<no key>"); target.append("<no key>");
} else { } else {
Iterator<String> keys = namesModifiable.iterator(); Iterator<String> keys = namesModifiable.iterator();
target.append(keys.next()).append(' '); target.append(keys.next()).append(' ');
if (keys.hasNext()) { if (keys.hasNext()) {
target.append('(').append(keys.next()); target.append('(').append(keys.next());
while (keys.hasNext()) { while (keys.hasNext()) {
target.append(" ,").append(keys.next()); target.append(" ,").append(keys.next());
} }
target.append(") "); target.append(") ");
} }
} }
} else { } else {
target.append("<root> "); target.append("<root> ");
} }
String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>"; String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : "<no command>";
target.append(commandClass); target.append(commandClass);
for (ChildCommandAddress child : new HashSet<>(children.values())) { for (ChildCommandAddress child : new HashSet<>(children.values())) {
child.appendDebugInformation(target, linePrefix + " ", seen); child.appendDebugInformation(target, linePrefix + " ", seen);
} }
} }
} }

View File

@@ -1,136 +1,136 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public class PermissionContextFilter implements IContextFilter { public class PermissionContextFilter implements IContextFilter {
private String permission; private String permission;
private String[] permissionComponents; private String[] permissionComponents;
private int componentInsertionIndex; private int componentInsertionIndex;
private String failMessage; private String failMessage;
public PermissionContextFilter(String permission) { public PermissionContextFilter(String permission) {
this.permission = Objects.requireNonNull(permission); this.permission = Objects.requireNonNull(permission);
} }
public PermissionContextFilter(String permission, String failMessage) { public PermissionContextFilter(String permission, String failMessage) {
this(permission); this(permission);
this.failMessage = failMessage; this.failMessage = failMessage;
} }
public PermissionContextFilter(String permission, boolean inheritable) { public PermissionContextFilter(String permission, boolean inheritable) {
this(permission, null, inheritable); this(permission, null, inheritable);
} }
public PermissionContextFilter(String permission, String failMessage, boolean inheritable) { public PermissionContextFilter(String permission, String failMessage, boolean inheritable) {
this(permission, failMessage); this(permission, failMessage);
if (inheritable) { if (inheritable) {
setupInheritability(-1); setupInheritability(-1);
} }
} }
public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) { public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) {
this(permission, failMessage); this(permission, failMessage);
setupInheritability(componentInsertionIndex); setupInheritability(componentInsertionIndex);
} }
private void setupInheritability(int componentInsertionIndex) { private void setupInheritability(int componentInsertionIndex) {
this.permissionComponents = permission.split("\\."); this.permissionComponents = permission.split("\\.");
this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex; this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex;
if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException(); if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException();
} }
private void doFilter(ExecutionContext context, String permission) throws CommandException { private void doFilter(ExecutionContext context, String permission) throws CommandException {
if (failMessage != null) { if (failMessage != null) {
Validate.isAuthorized(context.getSender(), permission, failMessage); Validate.isAuthorized(context.getSender(), permission, failMessage);
} else { } else {
Validate.isAuthorized(context.getSender(), permission); Validate.isAuthorized(context.getSender(), permission);
} }
} }
@Override @Override
public void filterContext(ExecutionContext context) throws CommandException { public void filterContext(ExecutionContext context) throws CommandException {
doFilter(context, permission); doFilter(context, permission);
} }
public String getInheritedPermission(String[] components) { public String getInheritedPermission(String[] components) {
int insertedAmount = components.length; int insertedAmount = components.length;
String[] currentComponents = permissionComponents; String[] currentComponents = permissionComponents;
int currentAmount = currentComponents.length; int currentAmount = currentComponents.length;
String[] targetArray = new String[currentAmount + insertedAmount]; String[] targetArray = new String[currentAmount + insertedAmount];
int insertionIndex; int insertionIndex;
//int newInsertionIndex; //int newInsertionIndex;
if (componentInsertionIndex == -1) { if (componentInsertionIndex == -1) {
insertionIndex = currentAmount; insertionIndex = currentAmount;
//newInsertionIndex = -1; //newInsertionIndex = -1;
} else { } else {
insertionIndex = componentInsertionIndex; insertionIndex = componentInsertionIndex;
//newInsertionIndex = insertionIndex + insertedAmount; //newInsertionIndex = insertionIndex + insertedAmount;
} }
// copy the current components up to insertionIndex // copy the current components up to insertionIndex
System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex); System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex);
// copy the new components into the array at insertionIndex // copy the new components into the array at insertionIndex
System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount); System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount);
// copy the current components from insertionIndex + inserted amount // copy the current components from insertionIndex + inserted amount
System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex); System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex);
return String.join(".", targetArray); return String.join(".", targetArray);
} }
@Override @Override
public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException {
if (isInheritable()) { if (isInheritable()) {
doFilter(subContext, getInheritedPermission(path)); doFilter(subContext, getInheritedPermission(path));
} }
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return Priority.PERMISSION; return Priority.PERMISSION;
} }
public boolean isInheritable() { public boolean isInheritable() {
return permissionComponents != null; return permissionComponents != null;
} }
public String getPermission() { public String getPermission() {
return permission; return permission;
} }
public int getComponentInsertionIndex() { public int getComponentInsertionIndex() {
return componentInsertionIndex; return componentInsertionIndex;
} }
public String getFailMessage() { public String getFailMessage() {
return failMessage; return failMessage;
} }
/* /*
private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf()) private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf())
private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> { private fun getPermissionsOf(address: ICommandAddress, path: Array<String>, result: MutableList<String>): List<String> {
val command = address.command ?: return result val command = address.command ?: return result
var inherited = false var inherited = false
for (filter in command.contextFilters) { for (filter in command.contextFilters) {
when (filter) { when (filter) {
is PermissionContextFilter -> { is PermissionContextFilter -> {
if (path.isEmpty()) result.add(filter.permission) if (path.isEmpty()) result.add(filter.permission)
else if (filter.isInheritable) result.add(filter.getInheritedPermission(path)) else if (filter.isInheritable) result.add(filter.getInheritedPermission(path))
} }
is InheritingContextFilter -> { is InheritingContextFilter -> {
if (filter.priority == PERMISSION && address.hasParent() && !inherited) { if (filter.priority == PERMISSION && address.hasParent() && !inherited) {
inherited = true inherited = true
getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result) getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result)
} }
} }
} }
} }
return result return result
} }
*/ */
} }

View File

@@ -76,7 +76,7 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
} }
private static void debugChildren(ModifiableCommandAddress address) { public static void debugChildren(ModifiableCommandAddress address) {
Collection<String> keys = address.getChildrenMainKeys(); Collection<String> keys = address.getChildrenMainKeys();
for (String key : keys) { for (String key : keys) {
ChildCommandAddress child = address.getChild(key); ChildCommandAddress child = address.getChild(key);
@@ -233,18 +233,19 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
@Override @Override
public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) { public List<String> getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) {
ExecutionContext context = new ExecutionContext(sender, buffer, true); ExecutionContext context = new ExecutionContext(sender, buffer, true);
long start = System.currentTimeMillis();
try { try {
ICommandAddress target = getCommandTarget(context, buffer); ICommandAddress target = getCommandTarget(context, buffer);
List<String> out; List<String> out = Collections.emptyList();
if (target.hasCommand()) { /*if (target.hasCommand()) {
context.setCommand(target.getCommand()); context.setCommand(target.getCommand());
target.getCommand().initializeAndFilterContext(context); target.getCommand().initializeAndFilterContext(context);
out = target.getCommand().tabComplete(sender, context, location); out = target.getCommand().tabComplete(sender, context, location);
} else { } else {
out = Collections.emptyList(); out = Collections.emptyList();
} }*/
int cursor = buffer.getCursor(); int cursor = buffer.getCursor();
String input; String input;
@@ -269,6 +270,11 @@ public class RootCommandAddress extends ModifiableCommandAddress implements ICom
} catch (CommandException ex) { } catch (CommandException ex) {
return Collections.emptyList(); return Collections.emptyList();
} finally {
long duration = System.currentTimeMillis() - start;
if (duration > 2) {
System.out.println(String.format("Complete took %.3f seconds", duration / 1000.0));
}
} }
} }

View File

@@ -1,52 +1,52 @@
package io.dico.dicore.command; package io.dico.dicore.command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.Optional; import java.util.Optional;
public class Validate { public class Validate {
private Validate() { private Validate() {
} }
//@Contract("false, _ -> fail") //@Contract("false, _ -> fail")
public static void isTrue(boolean expression, String failMessage) throws CommandException { public static void isTrue(boolean expression, String failMessage) throws CommandException {
if (!expression) { if (!expression) {
throw new CommandException(failMessage); throw new CommandException(failMessage);
} }
} }
//@Contract("null, _ -> fail") //@Contract("null, _ -> fail")
public static void notNull(Object obj, String failMessage) throws CommandException { public static void notNull(Object obj, String failMessage) throws CommandException {
Validate.isTrue(obj != null, failMessage); Validate.isTrue(obj != null, failMessage);
} }
public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException { public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException {
Validate.isTrue(sender.hasPermission(permission), failMessage); Validate.isTrue(sender.hasPermission(permission), failMessage);
} }
public static void isAuthorized(CommandSender sender, String permission) throws CommandException { public static void isAuthorized(CommandSender sender, String permission) throws CommandException {
Validate.isAuthorized(sender, permission, "You do not have permission to use that command"); Validate.isAuthorized(sender, permission, "You do not have permission to use that command");
} }
//@Contract("null -> fail") //@Contract("null -> fail")
public static void isPlayer(CommandSender sender) throws CommandException { public static void isPlayer(CommandSender sender) throws CommandException {
isTrue(sender instanceof Player, "That command can only be used by players"); isTrue(sender instanceof Player, "That command can only be used by players");
} }
//@Contract("null -> fail") //@Contract("null -> fail")
public static void isConsole(CommandSender sender) throws CommandException { public static void isConsole(CommandSender sender) throws CommandException {
isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console"); isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console");
} }
public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException { public static <T> T returnIfPresent(Optional<T> maybe, String failMessage) throws CommandException {
if (!maybe.isPresent()) { if (!maybe.isPresent()) {
throw new CommandException(failMessage); throw new CommandException(failMessage);
} }
return maybe.get(); return maybe.get();
} }
} }

View File

@@ -1,52 +1,52 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import io.dico.dicore.command.parameter.type.ParameterConfig; import io.dico.dicore.command.parameter.type.ParameterConfig;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface BigRange { public @interface BigRange {
Class<?> MEMORY_CLASS = Memory.class; Class<?> MEMORY_CLASS = Memory.class;
ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class); ParameterConfig<BigRange, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class);
Memory DEFAULT = new Memory("MIN", "MAX", "0"); Memory DEFAULT = new Memory("MIN", "MAX", "0");
String min() default "MIN"; String min() default "MIN";
String max() default "MAX"; String max() default "MAX";
String defaultValue() default "0"; String defaultValue() default "0";
class Memory { class Memory {
private final String min; private final String min;
private final String max; private final String max;
private final String defaultValue; private final String defaultValue;
public Memory(BigRange range) { public Memory(BigRange range) {
this(range.min(), range.max(), range.defaultValue()); this(range.min(), range.max(), range.defaultValue());
} }
public Memory(String min, String max, String defaultValue) { public Memory(String min, String max, String defaultValue) {
this.min = min; this.min = min;
this.max = max; this.max = max;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
public String min() { public String min() {
return min; return min;
} }
public String max() { public String max() {
return max; return max;
} }
public String defaultValue() { public String defaultValue() {
return defaultValue; return defaultValue;
} }
} }
} }

View File

@@ -1,16 +1,16 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface Cmd { public @interface Cmd {
String value(); String value();
String[] aliases() default {}; String[] aliases() default {};
} }

View File

@@ -1,27 +1,27 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import io.dico.dicore.command.parameter.type.IParameterTypeSelector; import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands. * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands.
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface CmdParamType { public @interface CmdParamType {
/** /**
* If this flag is set, the type is registered without its annotation type. * If this flag is set, the type is registered without its annotation type.
* As a result, the {@link IParameterTypeSelector} is more likely to select it (faster). * As a result, the {@link IParameterTypeSelector} is more likely to select it (faster).
* This is irrelevant if there is no annotation type or param config. * This is irrelevant if there is no annotation type or param config.
* *
* @return true if this parameter type should be registered without its annotation type too * @return true if this parameter type should be registered without its annotation type too
*/ */
boolean infolessAlias() default false; boolean infolessAlias() default false;
} }

View File

@@ -1,35 +1,35 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
public class CommandAnnotationUtils { public class CommandAnnotationUtils {
/** /**
* Get the short description from a {@link Desc} annotation. * Get the short description from a {@link Desc} annotation.
* If {@link Desc#shortVersion()} is given, returns that. * If {@link Desc#shortVersion()} is given, returns that.
* Otherwise, returns the first element of {@link Desc#value()} * Otherwise, returns the first element of {@link Desc#value()}
* If neither is available, returns null. * If neither is available, returns null.
* *
* @param desc the annotation * @param desc the annotation
* @return the short description * @return the short description
*/ */
public static String getShortDescription(Desc desc) { public static String getShortDescription(Desc desc) {
String descString; String descString;
if (desc == null) { if (desc == null) {
descString = null; descString = null;
} else if (!desc.shortVersion().isEmpty()) { } else if (!desc.shortVersion().isEmpty()) {
descString = desc.shortVersion(); descString = desc.shortVersion();
} else if (desc.value().length > 0) { } else if (desc.value().length > 0) {
descString = desc.value()[0]; descString = desc.value()[0];
if (desc.value().length > 1) { if (desc.value().length > 1) {
//System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier); //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier);
} }
if (descString != null && descString.isEmpty()) { if (descString != null && descString.isEmpty()) {
descString = null; descString = null;
} }
} else { } else {
descString = null; descString = null;
} }
return descString; return descString;
} }
} }

View File

@@ -1,27 +1,27 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Desc { public @interface Desc {
/** /**
* Multiline description if {@link #shortVersion} is set. * Multiline description if {@link #shortVersion} is set.
* Otherwise, this should be an array with one element (aka, you don't have to add array brackets). * Otherwise, this should be an array with one element (aka, you don't have to add array brackets).
* *
* @return the multiline description. * @return the multiline description.
* @see CommandAnnotationUtils#getShortDescription(Desc) * @see CommandAnnotationUtils#getShortDescription(Desc)
*/ */
String[] value(); String[] value();
/** /**
* Short description, use if {@link #value} is multi-line. * Short description, use if {@link #value} is multi-line.
* To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)} * To get a short description from a {@link Desc}, you should use {@link CommandAnnotationUtils#getShortDescription(Desc)}
* *
* @return short description * @return short description
* @see CommandAnnotationUtils#getShortDescription(Desc) * @see CommandAnnotationUtils#getShortDescription(Desc)
*/ */
String shortVersion() default ""; String shortVersion() default "";
} }

View File

@@ -1,16 +1,16 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface Flag { public @interface Flag {
String value() default ""; String value() default "";
String permission() default ""; String permission() default "";
} }

View File

@@ -1,14 +1,14 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface GenerateCommands { public @interface GenerateCommands {
String[] value() default {"help"}; String[] value() default {"help"};
} }

View File

@@ -1,68 +1,68 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* Annotation to define sub-groups of the group registered reflectively from all methods in a class. * Annotation to define sub-groups of the group registered reflectively from all methods in a class.
* <p> * <p>
* Commands are selected for grouping by matching their method's names to a regular expression. * Commands are selected for grouping by matching their method's names to a regular expression.
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface GroupMatchedCommands { public @interface GroupMatchedCommands {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@interface GroupEntry { @interface GroupEntry {
/** /**
* Regular expression to match method names for this group * Regular expression to match method names for this group
* Must be non-empty * Must be non-empty
* *
* @return the regular expression * @return the regular expression
*/ */
String regex(); String regex();
/** /**
* The name or main key of the sub-group or address * The name or main key of the sub-group or address
* Must be non-empty * Must be non-empty
* *
* @return the group name * @return the group name
*/ */
String group(); String group();
/** /**
* The aliases for the sub-group * The aliases for the sub-group
* *
* @return the group aliases * @return the group aliases
*/ */
String[] groupAliases() default {}; String[] groupAliases() default {};
/** /**
* Generated (predefined) commands for the sub-group * Generated (predefined) commands for the sub-group
*/ */
String[] generatedCommands() default {}; String[] generatedCommands() default {};
/** /**
* @see Desc * @see Desc
*/ */
String[] description() default {}; String[] description() default {};
/** /**
* @see Desc * @see Desc
*/ */
String shortDescription() default ""; String shortDescription() default "";
} }
/** /**
* The defined groups. * The defined groups.
* If a method name matches the regex of multiple groups, * If a method name matches the regex of multiple groups,
* groups are prioritized by the order in which they appear in this array. * groups are prioritized by the order in which they appear in this array.
* *
* @return the defined groups * @return the defined groups
*/ */
GroupEntry[] value(); GroupEntry[] value();
} }

View File

@@ -1,14 +1,14 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface NamedArg { public @interface NamedArg {
String value(); String value();
} }

View File

@@ -1,17 +1,17 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Deprecated @Deprecated
public @interface PreprocessArgs { public @interface PreprocessArgs {
String tokens() default "\"\""; String tokens() default "\"\"";
char escapeChar() default '\\'; char escapeChar() default '\\';
} }

View File

@@ -1,67 +1,67 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.Validate; import io.dico.dicore.command.Validate;
import io.dico.dicore.command.parameter.type.ParameterConfig; import io.dico.dicore.command.parameter.type.ParameterConfig;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
public @interface Range { public @interface Range {
Class<?> MEMORY_CLASS = Memory.class; Class<?> MEMORY_CLASS = Memory.class;
ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class); ParameterConfig<Range, Memory> CONFIG = ParameterConfig.getMemoryClassFromField(Range.class);
Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0); Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0);
double min() default -Double.MAX_VALUE; double min() default -Double.MAX_VALUE;
double max() default Double.MAX_VALUE; double max() default Double.MAX_VALUE;
double defaultValue() default 0; double defaultValue() default 0;
class Memory { class Memory {
private final double min; private final double min;
private final double max; private final double max;
private final double defaultValue; private final double defaultValue;
public Memory(Range range) { public Memory(Range range) {
this(range.min(), range.max(), range.defaultValue()); this(range.min(), range.max(), range.defaultValue());
} }
public Memory(double min, double max, double defaultValue) { public Memory(double min, double max, double defaultValue) {
this.min = min; this.min = min;
this.max = max; this.max = max;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
public double min() { public double min() {
return min; return min;
} }
public double max() { public double max() {
return max; return max;
} }
public double defaultValue() { public double defaultValue() {
return defaultValue; return defaultValue;
} }
public void validate(Number x, String failMessage) throws CommandException { public void validate(Number x, String failMessage) throws CommandException {
Validate.isTrue(valid(x), failMessage); Validate.isTrue(valid(x), failMessage);
} }
public boolean valid(Number x) { public boolean valid(Number x) {
double d = x.doubleValue(); double d = x.doubleValue();
return min <= d && d <= max; return min <= d && d <= max;
} }
public boolean isDefault() { public boolean isDefault() {
return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue); return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue);
} }
} }
} }

View File

@@ -1,11 +1,11 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface RequireConsole { public @interface RequireConsole {
} }

View File

@@ -1,14 +1,14 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface RequireParameters { public @interface RequireParameters {
int value(); int value();
} }

View File

@@ -1,33 +1,33 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import io.dico.dicore.command.IContextFilter; import io.dico.dicore.command.IContextFilter;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* If this annotation is not present, inheriting permissions is default. * If this annotation is not present, inheriting permissions is default.
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface RequirePermissions { public @interface RequirePermissions {
/** /**
* Any permissions that must be present on the sender * Any permissions that must be present on the sender
* *
* @return an array of permission nodes * @return an array of permission nodes
*/ */
String[] value(); String[] value();
/** /**
* Whether permissions should (also) be inherited from the parent. * Whether permissions should (also) be inherited from the parent.
* This uses {@link IContextFilter#INHERIT_PERMISSIONS} * This uses {@link IContextFilter#INHERIT_PERMISSIONS}
* This is true by default. * This is true by default.
* *
* @return true if permissions should be inherited. * @return true if permissions should be inherited.
*/ */
boolean inherit() default true; boolean inherit() default true;
} }

View File

@@ -1,11 +1,11 @@
package io.dico.dicore.command.annotation; package io.dico.dicore.command.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface RequirePlayer { public @interface RequirePlayer {
} }

View File

@@ -1,94 +1,94 @@
package io.dico.dicore.command.chat; package io.dico.dicore.command.chat;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.HelpPages; import io.dico.dicore.command.chat.help.HelpPages;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class AbstractChatHandler implements IChatHandler { public class AbstractChatHandler implements IChatHandler {
private @NotNull HelpPages helpPages; private @NotNull HelpPages helpPages;
public AbstractChatHandler(@NotNull HelpPages helpPages) { public AbstractChatHandler(@NotNull HelpPages helpPages) {
this.helpPages = helpPages; this.helpPages = helpPages;
} }
public AbstractChatHandler() { public AbstractChatHandler() {
this(HelpPages.newDefaultHelpPages()); this(HelpPages.newDefaultHelpPages());
} }
@NotNull @NotNull
public HelpPages getHelpPages() { public HelpPages getHelpPages() {
return helpPages; return helpPages;
} }
public void setHelpPages(@NotNull HelpPages helpPages) { public void setHelpPages(@NotNull HelpPages helpPages) {
this.helpPages = helpPages; this.helpPages = helpPages;
} }
@Override @Override
public Formatting getChatFormatForType(EMessageType type) { public Formatting getChatFormatForType(EMessageType type) {
switch (type) { switch (type) {
case EXCEPTION: case EXCEPTION:
case BAD_NEWS: case BAD_NEWS:
return Formatting.RED; return Formatting.RED;
case INSTRUCTION: case INSTRUCTION:
case NEUTRAL: case NEUTRAL:
return Formatting.GRAY; return Formatting.GRAY;
case CUSTOM: case CUSTOM:
return Formatting.WHITE; return Formatting.WHITE;
case INFORMATIVE: case INFORMATIVE:
return Formatting.AQUA; return Formatting.AQUA;
case RESULT: case RESULT:
default: default:
case GOOD_NEWS: case GOOD_NEWS:
return Formatting.GREEN; return Formatting.GREEN;
case WARNING: case WARNING:
return Formatting.YELLOW; return Formatting.YELLOW;
case DESCRIPTION: case DESCRIPTION:
return Formatting.GREEN; return Formatting.GREEN;
case SYNTAX: case SYNTAX:
return Formatting.AQUA; return Formatting.AQUA;
case HIGHLIGHT: case HIGHLIGHT:
return Formatting.RED; return Formatting.RED;
case SUBCOMMAND: case SUBCOMMAND:
return Formatting.GRAY; return Formatting.GRAY;
case NUMBER: case NUMBER:
return Formatting.YELLOW; return Formatting.YELLOW;
} }
} }
@Override @Override
public String getMessagePrefixForType(EMessageType type) { public String getMessagePrefixForType(EMessageType type) {
return ""; return "";
} }
protected String createMessage(EMessageType type, String message) { protected String createMessage(EMessageType type, String message) {
if (message == null || message.isEmpty()) return null; if (message == null || message.isEmpty()) return null;
return getMessagePrefixForType(type) + getChatFormatForType(type) + message; return getMessagePrefixForType(type) + getChatFormatForType(type) + message;
} }
@Override @Override
public String createMessage(ExecutionContext context, EMessageType type, String message) { public String createMessage(ExecutionContext context, EMessageType type, String message) {
return createMessage(type, message); return createMessage(type, message);
} }
@Override @Override
public String createMessage(CommandSender sender, EMessageType type, String message) { public String createMessage(CommandSender sender, EMessageType type, String message) {
return createMessage(type, message); return createMessage(type, message);
} }
@Override @Override
public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
return helpPages.getHelpPage(sender, context, address, page); return helpPages.getHelpPage(sender, context, address, page);
} }
@Override @Override
public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
return helpPages.getSyntax(sender, context, address); return helpPages.getSyntax(sender, context, address);
} }
} }

View File

@@ -1,20 +1,20 @@
package io.dico.dicore.command.chat; package io.dico.dicore.command.chat;
/** /**
* Static factory methods for {@link IChatHandler} * Static factory methods for {@link IChatHandler}
*/ */
public class ChatHandlers { public class ChatHandlers {
private static final IChatHandler defaultChat; private static final IChatHandler defaultChat;
private ChatHandlers() { private ChatHandlers() {
} }
public static IChatHandler defaultChat() { public static IChatHandler defaultChat() {
return defaultChat; return defaultChat;
} }
static { static {
defaultChat = new AbstractChatHandler(); defaultChat = new AbstractChatHandler();
} }
} }

View File

@@ -1,60 +1,60 @@
package io.dico.dicore.command.chat; package io.dico.dicore.command.chat;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
//TODO add methods to send JSON messages //TODO add methods to send JSON messages
public interface IChatHandler { public interface IChatHandler {
default void sendMessage(ExecutionContext context, EMessageType type, String message) { default void sendMessage(ExecutionContext context, EMessageType type, String message) {
message = createMessage(context, type, message); message = createMessage(context, type, message);
if (message != null) { if (message != null) {
context.getSender().sendMessage(message); context.getSender().sendMessage(message);
} }
} }
default void sendMessage(CommandSender sender, EMessageType type, String message) { default void sendMessage(CommandSender sender, EMessageType type, String message) {
message = createMessage(sender, type, message); message = createMessage(sender, type, message);
if (message != null) { if (message != null) {
sender.sendMessage(message); sender.sendMessage(message);
} }
} }
default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) { default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) {
sendMessage(context, EMessageType.EXCEPTION, exception.getMessage()); sendMessage(context, EMessageType.EXCEPTION, exception.getMessage());
} }
default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) { default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) {
if (exception instanceof CommandException) { if (exception instanceof CommandException) {
handleCommandException(sender, context, (CommandException) exception); handleCommandException(sender, context, (CommandException) exception);
} else { } else {
sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command"); sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command");
exception.printStackTrace(); exception.printStackTrace();
} }
} }
default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) {
sender.sendMessage(createHelpMessage(sender, context, address, page)); sender.sendMessage(createHelpMessage(sender, context, address, page));
} }
default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) {
sender.sendMessage(createSyntaxMessage(sender, context, address)); sender.sendMessage(createSyntaxMessage(sender, context, address));
} }
Formatting getChatFormatForType(EMessageType type); Formatting getChatFormatForType(EMessageType type);
String getMessagePrefixForType(EMessageType type); String getMessagePrefixForType(EMessageType type);
String createMessage(ExecutionContext context, EMessageType type, String message); String createMessage(ExecutionContext context, EMessageType type, String message);
String createMessage(CommandSender sender, EMessageType type, String message); String createMessage(CommandSender sender, EMessageType type, String message);
String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page); String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page);
String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address); String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address);
} }

View File

@@ -1,92 +1,92 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.defaults.*; import io.dico.dicore.command.chat.help.defaults.*;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class HelpPages { public class HelpPages {
private @NotNull IPageBuilder pageBuilder; private @NotNull IPageBuilder pageBuilder;
private @NotNull IPageLayout pageLayout; private @NotNull IPageLayout pageLayout;
private int pageLength; private int pageLength;
private @NotNull List<IHelpTopic> helpTopics; private @NotNull List<IHelpTopic> helpTopics;
private @NotNull IHelpTopic syntaxTopic; private @NotNull IHelpTopic syntaxTopic;
public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) { public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List<IHelpTopic> helpTopics) {
this.pageBuilder = pageBuilder; this.pageBuilder = pageBuilder;
this.pageLayout = pageLayout; this.pageLayout = pageLayout;
this.pageLength = pageLength; this.pageLength = pageLength;
this.syntaxTopic = syntaxTopic; this.syntaxTopic = syntaxTopic;
this.helpTopics = helpTopics; this.helpTopics = helpTopics;
} }
public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) { public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) {
this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics))); this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics)));
} }
@SuppressWarnings("RedundantArrayCreation") @SuppressWarnings("RedundantArrayCreation")
public static HelpPages newDefaultHelpPages() { public static HelpPages newDefaultHelpPages() {
IHelpTopic syntaxTopic = new SyntaxHelpTopic(); IHelpTopic syntaxTopic = new SyntaxHelpTopic();
return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12, return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12,
syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()}); syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()});
} }
public @NotNull IPageBuilder getPageBuilder() { public @NotNull IPageBuilder getPageBuilder() {
return pageBuilder; return pageBuilder;
} }
public void setPageBuilder(@NotNull IPageBuilder pageBuilder) { public void setPageBuilder(@NotNull IPageBuilder pageBuilder) {
this.pageBuilder = pageBuilder; this.pageBuilder = pageBuilder;
} }
public @NotNull IPageLayout getPageLayout() { public @NotNull IPageLayout getPageLayout() {
return pageLayout; return pageLayout;
} }
public void setPageLayout(@NotNull IPageLayout pageLayout) { public void setPageLayout(@NotNull IPageLayout pageLayout) {
this.pageLayout = pageLayout; this.pageLayout = pageLayout;
} }
public int getPageLength() { public int getPageLength() {
return pageLength; return pageLength;
} }
public void setPageLength(int pageLength) { public void setPageLength(int pageLength) {
this.pageLength = pageLength; this.pageLength = pageLength;
} }
public @NotNull IHelpTopic getSyntaxTopic() { public @NotNull IHelpTopic getSyntaxTopic() {
return syntaxTopic; return syntaxTopic;
} }
public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) { public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) {
this.syntaxTopic = syntaxTopic; this.syntaxTopic = syntaxTopic;
} }
@NotNull @NotNull
public List<IHelpTopic> getHelpTopics() { public List<IHelpTopic> getHelpTopics() {
return helpTopics; return helpTopics;
} }
public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) { public void setHelpTopics(@NotNull List<IHelpTopic> helpTopics) {
this.helpTopics = helpTopics; this.helpTopics = helpTopics;
} }
public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) { public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) {
return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength); return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength);
} }
public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) { public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) {
List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false); List<IHelpComponent> components = syntaxTopic.getComponents(address, viewer, context, false);
if (components.isEmpty()) { if (components.isEmpty()) {
return getHelpPage(viewer, context, address, 1); return getHelpPage(viewer, context, address, 1);
} }
return DefaultPageBuilder.combine(components); return DefaultPageBuilder.combine(components);
} }
} }

View File

@@ -1,24 +1,24 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public abstract class HelpTopicModifier implements IHelpTopic { public abstract class HelpTopicModifier implements IHelpTopic {
private final IHelpTopic delegate; private final IHelpTopic delegate;
public HelpTopicModifier(IHelpTopic delegate) { public HelpTopicModifier(IHelpTopic delegate) {
this.delegate = Objects.requireNonNull(delegate); this.delegate = Objects.requireNonNull(delegate);
} }
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context); return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context);
} }
protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context); protected abstract List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context);
} }

View File

@@ -1,9 +1,9 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
public interface IHelpComponent { public interface IHelpComponent {
int lineCount(); int lineCount();
void appendTo(StringBuilder sb); void appendTo(StringBuilder sb);
} }

View File

@@ -1,23 +1,23 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
public interface IHelpTopic { public interface IHelpTopic {
/** /**
* Get the components of this help topic * Get the components of this help topic
* *
* @param target The address of the command to provide help about * @param target The address of the command to provide help about
* @param viewer The permissible that the page will be shown to (null -> choose a default set). * @param viewer The permissible that the page will be shown to (null -> choose a default set).
* @param context Context of the command execution * @param context Context of the command execution
* @param isForPage A boolean indicating if the components are to be used in a page (for help) * @param isForPage A boolean indicating if the components are to be used in a page (for help)
* @return a mutable list of components to include in the help pages * @return a mutable list of components to include in the help pages
*/ */
List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage); List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage);
} }

View File

@@ -1,7 +1,7 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
public interface IPageBorder extends IHelpComponent { public interface IPageBorder extends IHelpComponent {
void setPageCount(int pageCount); void setPageCount(int pageCount);
} }

View File

@@ -1,13 +1,13 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
public interface IPageBuilder { public interface IPageBuilder {
String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen); String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen);
} }

View File

@@ -1,20 +1,20 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
public interface IPageLayout { public interface IPageLayout {
/** /**
* Get the page borders for a help page * Get the page borders for a help page
* *
* @param target the address that help is displayed for * @param target the address that help is displayed for
* @param viewer the viewer of the help page, or null if irrelevant * @param viewer the viewer of the help page, or null if irrelevant
* @param context the context of the execution * @param context the context of the execution
* @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound) * @param pageNum the page number as displayed in the help page (so it's 1-bound and not 0-bound)
* @return the page borders. * @return the page borders.
*/ */
PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum); PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum);
} }

View File

@@ -1,76 +1,76 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
import java.util.Arrays; import java.util.Arrays;
public class PageBorders { public class PageBorders {
private final IPageBorder header, footer; private final IPageBorder header, footer;
public PageBorders(IPageBorder header, IPageBorder footer) { public PageBorders(IPageBorder header, IPageBorder footer) {
this.header = header; this.header = header;
this.footer = footer; this.footer = footer;
} }
public IPageBorder getHeader() { public IPageBorder getHeader() {
return header; return header;
} }
public IPageBorder getFooter() { public IPageBorder getFooter() {
return footer; return footer;
} }
public static IPageBorder simpleBorder(String... lines) { public static IPageBorder simpleBorder(String... lines) {
return new SimplePageBorder(lines); return new SimplePageBorder(lines);
} }
public static IPageBorder disappearingBorder(int pageNum, String... lines) { public static IPageBorder disappearingBorder(int pageNum, String... lines) {
return disappearingBorder(pageNum, 0, lines); return disappearingBorder(pageNum, 0, lines);
} }
public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) { public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) {
return new DisappearingPageBorder(pageNum, keptLines, lines); return new DisappearingPageBorder(pageNum, keptLines, lines);
} }
static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder { static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder {
private final String replacedSequence; private final String replacedSequence;
public SimplePageBorder(String replacedSequence, String... lines) { public SimplePageBorder(String replacedSequence, String... lines) {
super(lines); super(lines);
this.replacedSequence = replacedSequence; this.replacedSequence = replacedSequence;
} }
public SimplePageBorder(String... lines) { public SimplePageBorder(String... lines) {
super(lines); super(lines);
this.replacedSequence = "%pageCount%"; this.replacedSequence = "%pageCount%";
} }
@Override @Override
public void setPageCount(int pageCount) { public void setPageCount(int pageCount) {
String[] lines = this.lines; String[] lines = this.lines;
for (int i = 0; i < lines.length; i++) { for (int i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount)); lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount));
} }
} }
} }
static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder { static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder {
private final int pageNum; private final int pageNum;
private final int keptLines; private final int keptLines;
public DisappearingPageBorder(int pageNum, int keptLines, String... lines) { public DisappearingPageBorder(int pageNum, int keptLines, String... lines) {
super(lines); super(lines);
this.pageNum = pageNum; this.pageNum = pageNum;
this.keptLines = keptLines; this.keptLines = keptLines;
} }
@Override @Override
public void setPageCount(int pageCount) { public void setPageCount(int pageCount) {
if (pageCount == pageNum) { if (pageCount == pageNum) {
String[] lines = this.lines; String[] lines = this.lines;
this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length); this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length);
} }
} }
} }
} }

View File

@@ -1,27 +1,27 @@
package io.dico.dicore.command.chat.help; package io.dico.dicore.command.chat.help;
public class SimpleHelpComponent implements IHelpComponent { public class SimpleHelpComponent implements IHelpComponent {
String[] lines; String[] lines;
public SimpleHelpComponent(String... lines) { public SimpleHelpComponent(String... lines) {
this.lines = lines; this.lines = lines;
} }
@Override @Override
public int lineCount() { public int lineCount() {
return lines.length; return lines.length;
} }
@Override @Override
public void appendTo(StringBuilder sb) { public void appendTo(StringBuilder sb) {
String[] lines = this.lines; String[] lines = this.lines;
int len = lines.length; int len = lines.length;
if (0 < len) { if (0 < len) {
sb.append(lines[0]); sb.append(lines[0]);
} }
for (int i = 1; i < len; i++) { for (int i = 1; i < len; i++) {
sb.append('\n').append(lines[i]); sb.append('\n').append(lines[i]);
} }
} }
} }

View File

@@ -1,115 +1,115 @@
package io.dico.dicore.command.chat.help.defaults; package io.dico.dicore.command.chat.help.defaults;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.*; import io.dico.dicore.command.chat.help.*;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
public class DefaultPageBuilder implements IPageBuilder { public class DefaultPageBuilder implements IPageBuilder {
@Override @Override
public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target, public String getPage(List<IHelpTopic> helpTopics, IPageLayout pageLayout, ICommandAddress target,
Permissible viewer, ExecutionContext context, int pageNum, int pageLen) { Permissible viewer, ExecutionContext context, int pageNum, int pageLen) {
if (pageLen <= 0 || pageNum < 0) { if (pageLen <= 0 || pageNum < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
List<IHelpComponent> components = new LinkedList<>(); List<IHelpComponent> components = new LinkedList<>();
for (IHelpTopic topic : helpTopics) { for (IHelpTopic topic : helpTopics) {
components.addAll(topic.getComponents(target, viewer, context, true)); components.addAll(topic.getComponents(target, viewer, context, true));
} }
PageBorders pageBorders = null; PageBorders pageBorders = null;
int componentStartIdx = -1; int componentStartIdx = -1;
int componentEndIdx = -1; int componentEndIdx = -1;
int totalPageCount = 0; int totalPageCount = 0;
int curPageLines = 0; int curPageLines = 0;
ListIterator<IHelpComponent> iterator = components.listIterator(); ListIterator<IHelpComponent> iterator = components.listIterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
if (curPageLines == 0) { if (curPageLines == 0) {
if (pageBorders != null) { if (pageBorders != null) {
iterator.add(pageBorders.getFooter()); iterator.add(pageBorders.getFooter());
} }
if (pageNum == totalPageCount) { if (pageNum == totalPageCount) {
componentStartIdx = iterator.nextIndex(); componentStartIdx = iterator.nextIndex();
} else if (pageNum + 1 == totalPageCount) { } else if (pageNum + 1 == totalPageCount) {
componentEndIdx = iterator.nextIndex(); componentEndIdx = iterator.nextIndex();
} }
pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1); pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1);
if (pageBorders != null) { if (pageBorders != null) {
iterator.add(pageBorders.getHeader()); iterator.add(pageBorders.getHeader());
iterator.previous(); iterator.previous();
curPageLines += pageBorders.getFooter().lineCount(); curPageLines += pageBorders.getFooter().lineCount();
} }
totalPageCount++; totalPageCount++;
} }
IHelpComponent component = iterator.next(); IHelpComponent component = iterator.next();
int lineCount = component.lineCount(); int lineCount = component.lineCount();
curPageLines += lineCount; curPageLines += lineCount;
if (curPageLines >= pageLen) { if (curPageLines >= pageLen) {
curPageLines = 0; curPageLines = 0;
} }
} }
if (componentStartIdx == -1) { if (componentStartIdx == -1) {
// page does not exist // page does not exist
return ""; return "";
} }
if (componentEndIdx == -1) { if (componentEndIdx == -1) {
componentEndIdx = components.size(); componentEndIdx = components.size();
} }
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
iterator = components.listIterator(componentStartIdx); iterator = components.listIterator(componentStartIdx);
int count = componentEndIdx - componentStartIdx; int count = componentEndIdx - componentStartIdx;
boolean first = true; boolean first = true;
while (count-- > 0) { while (count-- > 0) {
IHelpComponent component = iterator.next(); IHelpComponent component = iterator.next();
if (component instanceof IPageBorder) { if (component instanceof IPageBorder) {
((IPageBorder) component).setPageCount(totalPageCount); ((IPageBorder) component).setPageCount(totalPageCount);
} }
if (first) { if (first) {
first = false; first = false;
} else { } else {
sb.append('\n'); sb.append('\n');
} }
component.appendTo(sb); component.appendTo(sb);
} }
return sb.toString(); return sb.toString();
} }
public static String combine(List<IHelpComponent> components) { public static String combine(List<IHelpComponent> components) {
StringBuilder rv = new StringBuilder(); StringBuilder rv = new StringBuilder();
Iterator<IHelpComponent> iterator = components.iterator(); Iterator<IHelpComponent> iterator = components.iterator();
if (iterator.hasNext()) { if (iterator.hasNext()) {
iterator.next().appendTo(rv); iterator.next().appendTo(rv);
} }
while (iterator.hasNext()) { while (iterator.hasNext()) {
rv.append('\n'); rv.append('\n');
iterator.next().appendTo(rv); iterator.next().appendTo(rv);
} }
return rv.toString(); return rv.toString();
} }
} }

View File

@@ -1,40 +1,40 @@
package io.dico.dicore.command.chat.help.defaults; package io.dico.dicore.command.chat.help.defaults;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.ModifiableCommandAddress; import io.dico.dicore.command.ModifiableCommandAddress;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.chat.IChatHandler; import io.dico.dicore.command.chat.IChatHandler;
import io.dico.dicore.command.chat.help.IPageBorder; import io.dico.dicore.command.chat.help.IPageBorder;
import io.dico.dicore.command.chat.help.IPageLayout; import io.dico.dicore.command.chat.help.IPageLayout;
import io.dico.dicore.command.chat.help.PageBorders; import io.dico.dicore.command.chat.help.PageBorders;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
public class DefaultPageLayout implements IPageLayout { public class DefaultPageLayout implements IPageLayout {
@Override @Override
public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) { public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) {
IChatHandler c = context.getAddress().getChatHandler(); IChatHandler c = context.getAddress().getChatHandler();
String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE); String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE);
Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE); Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE);
Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL); Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL);
String nextPageCommand; String nextPageCommand;
ICommandAddress executor = context.getAddress(); ICommandAddress executor = context.getAddress();
if (((ModifiableCommandAddress) executor).hasHelpCommand()) { if (((ModifiableCommandAddress) executor).hasHelpCommand()) {
nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1); nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1);
} else { } else {
nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1); nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1);
} }
String header = prefix + informative + "Help page " + number + pageNum + informative + String header = prefix + informative + "Help page " + number + pageNum + informative +
'/' + number + "%pageCount%" + informative + " for /" + target.getAddress(); '/' + number + "%pageCount%" + informative + " for /" + target.getAddress();
String footer = informative + "Type /" + nextPageCommand + " for the next page"; String footer = informative + "Type /" + nextPageCommand + " for the next page";
IPageBorder headerBorder = PageBorders.simpleBorder("", header); IPageBorder headerBorder = PageBorders.simpleBorder("", header);
IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer); IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer);
return new PageBorders(headerBorder, footerBorder); return new PageBorders(headerBorder, footerBorder);
} }
} }

View File

@@ -1,45 +1,45 @@
package io.dico.dicore.command.chat.help.defaults; package io.dico.dicore.command.chat.help.defaults;
import io.dico.dicore.command.Command; import io.dico.dicore.command.Command;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
import io.dico.dicore.command.chat.help.SimpleHelpComponent; import io.dico.dicore.command.chat.help.SimpleHelpComponent;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class DescriptionHelpTopic implements IHelpTopic { public class DescriptionHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
List<IHelpComponent> out = new ArrayList<>(); List<IHelpComponent> out = new ArrayList<>();
Formatting format = context.getFormat(EMessageType.DESCRIPTION); Formatting format = context.getFormat(EMessageType.DESCRIPTION);
if (!target.hasCommand()) { if (!target.hasCommand()) {
return out; return out;
} }
Command command = target.getCommand(); Command command = target.getCommand();
String[] description = command.getDescription(); String[] description = command.getDescription();
if (description.length == 0) { if (description.length == 0) {
String shortDescription = command.getShortDescription(); String shortDescription = command.getShortDescription();
if (shortDescription == null) { if (shortDescription == null) {
return out; return out;
} }
description = new String[]{shortDescription}; description = new String[]{shortDescription};
} }
for (int i = 0; i < description.length; i++) { for (int i = 0; i < description.length; i++) {
description[i] = format + description[i]; description[i] = format + description[i];
} }
out.add(new SimpleHelpComponent(description)); out.add(new SimpleHelpComponent(description));
return out; return out;
} }
} }

View File

@@ -1,59 +1,59 @@
package io.dico.dicore.command.chat.help.defaults; package io.dico.dicore.command.chat.help.defaults;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
import io.dico.dicore.command.chat.help.SimpleHelpComponent; import io.dico.dicore.command.chat.help.SimpleHelpComponent;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.*; import java.util.*;
public class SubcommandsHelpTopic implements IHelpTopic { public class SubcommandsHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
Collection<String> mainKeys = target.getChildrenMainKeys(); Collection<String> mainKeys = target.getChildrenMainKeys();
if (mainKeys.isEmpty()) { if (mainKeys.isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<IHelpComponent> result = new ArrayList<>(); List<IHelpComponent> result = new ArrayList<>();
mainKeys = new ArrayList<>(target.getChildrenMainKeys()); mainKeys = new ArrayList<>(target.getChildrenMainKeys());
((ArrayList<String>) mainKeys).sort(null); ((ArrayList<String>) mainKeys).sort(null);
CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender(); CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender();
for (String key : mainKeys) { for (String key : mainKeys) {
ICommandAddress child = target.getChild(key); ICommandAddress child = target.getChild(key);
if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) { if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) {
result.add(getComponent(child, viewer, context)); result.add(getComponent(child, viewer, context));
} }
} }
return result; return result;
} }
public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) { public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) {
Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND); Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND);
Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT); Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT);
String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey(); String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey();
String description = child.hasCommand() ? child.getCommand().getShortDescription() : null; String description = child.hasCommand() ? child.getCommand().getShortDescription() : null;
if (description != null) { if (description != null) {
Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION); Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION);
return new SimpleHelpComponent(address, descriptionFormat + description); return new SimpleHelpComponent(address, descriptionFormat + description);
} }
return new SimpleHelpComponent(address); return new SimpleHelpComponent(address);
} }
private static Formatting colorOf(ExecutionContext context, EMessageType type) { private static Formatting colorOf(ExecutionContext context, EMessageType type) {
return context.getAddress().getChatHandler().getChatFormatForType(type); return context.getAddress().getChatHandler().getChatFormatForType(type);
} }
} }

View File

@@ -1,92 +1,92 @@
package io.dico.dicore.command.chat.help.defaults; package io.dico.dicore.command.chat.help.defaults;
import io.dico.dicore.command.Command; import io.dico.dicore.command.Command;
import io.dico.dicore.command.EMessageType; import io.dico.dicore.command.EMessageType;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.Formatting; import io.dico.dicore.Formatting;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
import io.dico.dicore.command.chat.help.SimpleHelpComponent; import io.dico.dicore.command.chat.help.SimpleHelpComponent;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.ParameterList; import io.dico.dicore.command.parameter.ParameterList;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class SyntaxHelpTopic implements IHelpTopic { public class SyntaxHelpTopic implements IHelpTopic {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
if (!target.hasCommand()) { if (!target.hasCommand()) {
return Collections.emptyList(); return Collections.emptyList();
} }
if (target.hasChildren()) { if (target.hasChildren()) {
if (!isForPage) { if (!isForPage) {
// HelpPages will send help instead of syntax, which might in turn include syntax as well. // HelpPages will send help instead of syntax, which might in turn include syntax as well.
return Collections.emptyList(); return Collections.emptyList();
} }
if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) { if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) {
// no point adding syntax at all // no point adding syntax at all
return Collections.emptyList(); return Collections.emptyList();
} }
} }
StringBuilder line = new StringBuilder(); StringBuilder line = new StringBuilder();
if (isForPage) if (isForPage)
line.append(context.getFormat(EMessageType.SYNTAX)) line.append(context.getFormat(EMessageType.SYNTAX))
.append("Syntax: "); .append("Syntax: ");
line.append('/') line.append('/')
.append(context.getFormat(EMessageType.INSTRUCTION)) .append(context.getFormat(EMessageType.INSTRUCTION))
.append(target.getAddress()) .append(target.getAddress())
.append(' '); .append(' ');
addShortSyntax(line, target, context); addShortSyntax(line, target, context);
return Collections.singletonList(new SimpleHelpComponent(line.toString())); return Collections.singletonList(new SimpleHelpComponent(line.toString()));
} }
private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) { private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) {
if (address.hasCommand()) { if (address.hasCommand()) {
Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX); Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX);
Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT); Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT);
builder.append(syntaxColor); builder.append(syntaxColor);
Command command = address.getCommand(); Command command = address.getCommand();
ParameterList list = command.getParameterList(); ParameterList list = command.getParameterList();
Parameter<?, ?> repeated = list.getRepeatedParameter(); Parameter<?, ?> repeated = list.getRepeatedParameter();
int requiredCount = list.getRequiredCount(); int requiredCount = list.getRequiredCount();
List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters(); List<Parameter<?, ?>> indexedParameters = list.getIndexedParameters();
for (int i = 0, n = indexedParameters.size(); i < n; i++) { for (int i = 0, n = indexedParameters.size(); i < n; i++) {
builder.append(i < requiredCount ? " <" : " ["); builder.append(i < requiredCount ? " <" : " [");
Parameter<?, ?> param = indexedParameters.get(i); Parameter<?, ?> param = indexedParameters.get(i);
builder.append(param.getName()); builder.append(param.getName());
if (param == repeated) { if (param == repeated) {
builder.append(highlight).append("...").append(syntaxColor); builder.append(highlight).append("...").append(syntaxColor);
} }
builder.append(i < requiredCount ? '>' : ']'); builder.append(i < requiredCount ? '>' : ']');
} }
Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName(); Map<String, Parameter<?, ?>> parametersByName = list.getParametersByName();
for (Parameter<?, ?> param : parametersByName.values()) { for (Parameter<?, ?> param : parametersByName.values()) {
if (param.isFlag()) { if (param.isFlag()) {
builder.append(" [").append(param.getName()); builder.append(" [").append(param.getName());
if (param.expectsInput()) { if (param.expectsInput()) {
builder.append(" <").append(param.getName()).append(">"); builder.append(" <").append(param.getName()).append(">");
} }
builder.append(']'); builder.append(']');
} }
} }
} else { } else {
builder.append(' '); builder.append(' ');
} }
} }
} }

View File

@@ -1,29 +1,29 @@
package io.dico.dicore.command.chat.help.insertion; package io.dico.dicore.command.chat.help.insertion;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
public enum EInsertionStage implements IInsertionFunction { public enum EInsertionStage implements IInsertionFunction {
START { START {
@Override @Override
public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
return 0; return 0;
} }
}, },
CENTER { CENTER {
@Override @Override
public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
return current.size() / 2; return current.size() / 2;
} }
}, },
END { END {
@Override @Override
public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
return current.size(); return current.size();
} }
} }
} }

View File

@@ -1,43 +1,43 @@
package io.dico.dicore.command.chat.help.insertion; package io.dico.dicore.command.chat.help.insertion;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.HelpTopicModifier; import io.dico.dicore.command.chat.help.HelpTopicModifier;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class HelpComponentInserter extends HelpTopicModifier { public class HelpComponentInserter extends HelpTopicModifier {
private List<IInsertion> insertions = new ArrayList<>(); private List<IInsertion> insertions = new ArrayList<>();
public HelpComponentInserter(IHelpTopic delegate) { public HelpComponentInserter(IHelpTopic delegate) {
super(delegate); super(delegate);
} }
@Override @Override
protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) { protected List<IHelpComponent> modify(List<IHelpComponent> components, ICommandAddress target, Permissible viewer, ExecutionContext context) {
// int componentCount = components.size(); // int componentCount = components.size();
for (int i = insertions.size() - 1; i >= 0; i--) { for (int i = insertions.size() - 1; i >= 0; i--) {
IInsertion insertion = insertions.get(i); IInsertion insertion = insertions.get(i);
int idx = insertion.insertionIndex(components, target, viewer, context); int idx = insertion.insertionIndex(components, target, viewer, context);
List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true); List<IHelpComponent> inserted = insertion.getComponents(target, viewer, context, true);
components.addAll(idx, inserted); components.addAll(idx, inserted);
} }
return components; return components;
} }
public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) { public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) {
return insert(Insertions.combine(helpTopic, insertionFunction)); return insert(Insertions.combine(helpTopic, insertionFunction));
} }
public HelpComponentInserter insert(IInsertion insertion) { public HelpComponentInserter insert(IInsertion insertion) {
insertions.add(insertion); insertions.add(insertion);
return this; return this;
} }
} }

View File

@@ -1,7 +1,7 @@
package io.dico.dicore.command.chat.help.insertion; package io.dico.dicore.command.chat.help.insertion;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
interface IInsertion extends IHelpTopic, IInsertionFunction { interface IInsertion extends IHelpTopic, IInsertionFunction {
} }

View File

@@ -1,14 +1,14 @@
package io.dico.dicore.command.chat.help.insertion; package io.dico.dicore.command.chat.help.insertion;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
public interface IInsertionFunction { public interface IInsertionFunction {
int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context); int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context);
} }

View File

@@ -1,31 +1,31 @@
package io.dico.dicore.command.chat.help.insertion; package io.dico.dicore.command.chat.help.insertion;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.chat.help.IHelpComponent; import io.dico.dicore.command.chat.help.IHelpComponent;
import io.dico.dicore.command.chat.help.IHelpTopic; import io.dico.dicore.command.chat.help.IHelpTopic;
import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permissible;
import java.util.List; import java.util.List;
public class Insertions { public class Insertions {
private Insertions() { private Insertions() {
} }
public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) { public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) {
return new IInsertion() { return new IInsertion() {
@Override @Override
public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { public List<IHelpComponent> getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) {
return topic.getComponents(target, viewer, context, true); return topic.getComponents(target, viewer, context, true);
} }
@Override @Override
public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) { public int insertionIndex(List<IHelpComponent> current, ICommandAddress target, Permissible viewer, ExecutionContext context) {
return function.insertionIndex(current, target, viewer, context); return function.insertionIndex(current, target, viewer, context);
} }
}; };
} }
} }

View File

@@ -1,295 +1,295 @@
package io.dico.dicore.command.parameter; package io.dico.dicore.command.parameter;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
/** /**
* Buffer for the arguments. * Buffer for the arguments.
* Easy to traverse for the parser. * Easy to traverse for the parser.
*/ */
public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess { public class ArgumentBuffer extends AbstractList<String> implements Iterator<String>, RandomAccess {
private String[] array; private String[] array;
private int cursor = 0; // index of the next return value private int cursor = 0; // index of the next return value
private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy() private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy()
public ArgumentBuffer(String label, String[] args) { public ArgumentBuffer(String label, String[] args) {
this(combine(label, args)); this(combine(label, args));
} }
private static String[] combine(String label, String[] args) { private static String[] combine(String label, String[] args) {
String[] result; String[] result;
//if (args.length > 0 && "".equals(args[args.length - 1])) { //if (args.length > 0 && "".equals(args[args.length - 1])) {
// // drop the last element of args if it is empty // // drop the last element of args if it is empty
// result = args; // result = args;
//} else { //} else {
result = new String[args.length + 1]; result = new String[args.length + 1];
//} //}
System.arraycopy(args, 0, result, 1, result.length - 1); System.arraycopy(args, 0, result, 1, result.length - 1);
result[0] = Objects.requireNonNull(label); result[0] = Objects.requireNonNull(label);
return result; return result;
} }
/** /**
* Constructs a new ArgumentBuffer using the given array, without copying it first. * Constructs a new ArgumentBuffer using the given array, without copying it first.
* None of the array its elements should be empty. * None of the array its elements should be empty.
* *
* @param array the array * @param array the array
* @throws NullPointerException if the array or any of its elements are null * @throws NullPointerException if the array or any of its elements are null
*/ */
public ArgumentBuffer(String[] array) { public ArgumentBuffer(String[] array) {
for (String elem : array) { for (String elem : array) {
if (elem == null) throw new NullPointerException("ArgumentBuffer array element"); if (elem == null) throw new NullPointerException("ArgumentBuffer array element");
} }
this.array = array; this.array = array;
} }
public int getCursor() { public int getCursor() {
return cursor; return cursor;
} }
public @NotNull ArgumentBuffer setCursor(int cursor) { public @NotNull ArgumentBuffer setCursor(int cursor) {
if (cursor <= 0) { if (cursor <= 0) {
cursor = 0; cursor = 0;
} else if (size() <= cursor) { } else if (size() <= cursor) {
cursor = size(); cursor = size();
} }
this.cursor = cursor; this.cursor = cursor;
return this; return this;
} }
@Override @Override
public int size() { public int size() {
return array.length; return array.length;
} }
@Override @Override
public @NotNull String get(int index) { public @NotNull String get(int index) {
return array[index]; return array[index];
} }
public int nextIndex() { public int nextIndex() {
return cursor; return cursor;
} }
public int previousIndex() { public int previousIndex() {
return cursor - 1; return cursor - 1;
} }
public int remainingElements() { public int remainingElements() {
return size() - nextIndex() - 1; return size() - nextIndex() - 1;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return nextIndex() < size(); return nextIndex() < size();
} }
public boolean hasPrevious() { public boolean hasPrevious() {
return 0 <= previousIndex(); return 0 <= previousIndex();
} }
/** /**
* Unlike conventional ListIterator implementations, this returns null if there is no next element * Unlike conventional ListIterator implementations, this returns null if there is no next element
* *
* @return the next value, or null * @return the next value, or null
*/ */
@Override @Override
public @Nullable String next() { public @Nullable String next() {
return hasNext() ? get(cursor++) : null; return hasNext() ? get(cursor++) : null;
} }
public @NotNull String requireNext(String parameterName) throws CommandException { public @NotNull String requireNext(String parameterName) throws CommandException {
String next = next(); String next = next();
if (next == null) { if (next == null) {
throw CommandException.missingArgument(parameterName); throw CommandException.missingArgument(parameterName);
} }
return next; return next;
} }
// useful for completion code // useful for completion code
public @NotNull String nextOrEmpty() { public @NotNull String nextOrEmpty() {
return hasNext() ? get(cursor++) : ""; return hasNext() ? get(cursor++) : "";
} }
/** /**
* Unlike conventional ListIterator implementations, this returns null if there is no previous element * Unlike conventional ListIterator implementations, this returns null if there is no previous element
* *
* @return the previous value, or null * @return the previous value, or null
*/ */
public @Nullable String previous() { public @Nullable String previous() {
return hasPrevious() ? get(--cursor) : null; return hasPrevious() ? get(--cursor) : null;
} }
public @Nullable String peekNext() { public @Nullable String peekNext() {
return hasNext() ? get(cursor) : null; return hasNext() ? get(cursor) : null;
} }
public @Nullable String peekPrevious() { public @Nullable String peekPrevious() {
return hasPrevious() ? get(cursor - 1) : null; return hasPrevious() ? get(cursor - 1) : null;
} }
public @NotNull ArgumentBuffer advance() { public @NotNull ArgumentBuffer advance() {
return advance(1); return advance(1);
} }
public @NotNull ArgumentBuffer advance(int amount) { public @NotNull ArgumentBuffer advance(int amount) {
cursor = Math.min(Math.max(0, cursor + amount), size()); cursor = Math.min(Math.max(0, cursor + amount), size());
return this; return this;
} }
public @NotNull ArgumentBuffer rewind() { public @NotNull ArgumentBuffer rewind() {
return rewind(1); return rewind(1);
} }
public @NotNull ArgumentBuffer rewind(int amount) { public @NotNull ArgumentBuffer rewind(int amount) {
return advance(-amount); return advance(-amount);
} }
@NotNull String[] getArray() { @NotNull String[] getArray() {
return array; return array;
} }
public @NotNull String[] getArrayFromCursor() { public @NotNull String[] getArrayFromCursor() {
return getArrayFromIndex(cursor); return getArrayFromIndex(cursor);
} }
public @NotNull String[] getArrayFromIndex(int index) { public @NotNull String[] getArrayFromIndex(int index) {
return Arrays.copyOfRange(array, index, array.length); return Arrays.copyOfRange(array, index, array.length);
} }
public @NotNull String getRawInput() { public @NotNull String getRawInput() {
return String.join(" ", array); return String.join(" ", array);
} }
public @NotNull String[] toArray() { public @NotNull String[] toArray() {
return array.clone(); return array.clone();
} }
@Override @Override
public @NotNull Iterator<String> iterator() { public @NotNull Iterator<String> iterator() {
return this; return this;
} }
@Override @Override
public @NotNull ListIterator<String> listIterator() { public @NotNull ListIterator<String> listIterator() {
return new ListIterator<String>() { return new ListIterator<String>() {
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return ArgumentBuffer.this.hasNext(); return ArgumentBuffer.this.hasNext();
} }
@Override @Override
public String next() { public String next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
return ArgumentBuffer.this.next(); return ArgumentBuffer.this.next();
} }
@Override @Override
public boolean hasPrevious() { public boolean hasPrevious() {
return ArgumentBuffer.this.hasPrevious(); return ArgumentBuffer.this.hasPrevious();
} }
@Override @Override
public String previous() { public String previous() {
if (!hasPrevious()) { if (!hasPrevious()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
return ArgumentBuffer.this.previous(); return ArgumentBuffer.this.previous();
} }
@Override @Override
public int nextIndex() { public int nextIndex() {
return ArgumentBuffer.this.nextIndex(); return ArgumentBuffer.this.nextIndex();
} }
@Override @Override
public int previousIndex() { public int previousIndex() {
return ArgumentBuffer.this.previousIndex(); return ArgumentBuffer.this.previousIndex();
} }
@Override @Override
public void remove() { public void remove() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void set(String s) { public void set(String s) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void add(String s) { public void add(String s) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
}; };
} }
public void dropTrailingEmptyElements() { public void dropTrailingEmptyElements() {
int removeCount = 0; int removeCount = 0;
String[] array = this.array; String[] array = this.array;
for (int i = array.length - 1; i >= 0; i--) { for (int i = array.length - 1; i >= 0; i--) {
if ("".equals(array[i])) { if ("".equals(array[i])) {
removeCount++; removeCount++;
} }
} }
if (removeCount > 0) { if (removeCount > 0) {
String[] newArray = new String[array.length - removeCount]; String[] newArray = new String[array.length - removeCount];
System.arraycopy(array, 0, newArray, 0, newArray.length); System.arraycopy(array, 0, newArray, 0, newArray.length);
this.array = newArray; this.array = newArray;
if (cursor > newArray.length) { if (cursor > newArray.length) {
cursor = newArray.length; cursor = newArray.length;
} }
} }
} }
/** /**
* Preprocess this argument buffer with the given preprocessor * Preprocess this argument buffer with the given preprocessor
* *
* @param preProcessor preprocessor * @param preProcessor preprocessor
* @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed. * @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed.
*/ */
public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) { public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) {
return preProcessor.process(this, -1); return preProcessor.process(this, -1);
} }
/** /**
* Allows a piece of code to traverse this buffer without modifying its cursor. * Allows a piece of code to traverse this buffer without modifying its cursor.
* After this method has been called for the first time on this instance, if this method * After this method has been called for the first time on this instance, if this method
* or the {@link #clone()} method are called, the operation carried out on the prior result has finished. * or the {@link #clone()} method are called, the operation carried out on the prior result has finished.
* As such, the same instance might be returned again. * As such, the same instance might be returned again.
* *
* @return A view of this buffer that doesn't affect this buffer's cursor. * @return A view of this buffer that doesn't affect this buffer's cursor.
*/ */
public ArgumentBuffer getUnaffectingCopy() { public ArgumentBuffer getUnaffectingCopy() {
// the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference. // the copy doesn't alter the cursor of this ArgumentBuffer when moved, but traverses the same array reference.
// there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method. // there is only ever one copy of an ArgumentBuffer, the cursor of which is updated on every call to this method.
ArgumentBuffer unaffectingCopy = this.unaffectingCopy; ArgumentBuffer unaffectingCopy = this.unaffectingCopy;
if (unaffectingCopy == null) { if (unaffectingCopy == null) {
this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array); this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array);
} }
unaffectingCopy.cursor = this.cursor; unaffectingCopy.cursor = this.cursor;
return unaffectingCopy; return unaffectingCopy;
} }
@SuppressWarnings("MethodDoesntCallSuperMethod") @SuppressWarnings("MethodDoesntCallSuperMethod")
public @NotNull ArgumentBuffer clone() { public @NotNull ArgumentBuffer clone() {
ArgumentBuffer result = getUnaffectingCopy(); ArgumentBuffer result = getUnaffectingCopy();
this.unaffectingCopy = null; this.unaffectingCopy = null;
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor()); return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor());
} }
} }

View File

@@ -1,177 +1,177 @@
package io.dico.dicore.command.parameter; package io.dico.dicore.command.parameter;
public class ArgumentMergingPreProcessor implements IArgumentPreProcessor { public class ArgumentMergingPreProcessor implements IArgumentPreProcessor {
private final String tokens; private final String tokens;
private final char escapeChar; private final char escapeChar;
public ArgumentMergingPreProcessor(String tokens, char escapeChar) { public ArgumentMergingPreProcessor(String tokens, char escapeChar) {
if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException(); if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException();
this.tokens = tokens; this.tokens = tokens;
this.escapeChar = escapeChar; this.escapeChar = escapeChar;
} }
@Override @Override
public ArgumentBuffer process(ArgumentBuffer buffer, int count) { public ArgumentBuffer process(ArgumentBuffer buffer, int count) {
Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count); Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count);
String[] array = parser.doProcess(); String[] array = parser.doProcess();
ArgumentBuffer result = new ArgumentBuffer(array); ArgumentBuffer result = new ArgumentBuffer(array);
parser.updateBuffer(result); parser.updateBuffer(result);
return result; return result;
} }
private class Parser { private class Parser {
private final String[] args; private final String[] args;
private final int start; private final int start;
private final int count; private final int count;
private int foundSectionCount; private int foundSectionCount;
private int currentIndex; private int currentIndex;
private int sectionStart; private int sectionStart;
private char closingToken; private char closingToken;
private int sectionEnd; private int sectionEnd;
private int removeCount; private int removeCount;
Parser(String[] args, int start, int count) { Parser(String[] args, int start, int count) {
this.start = start; this.start = start;
this.args = args; this.args = args;
this.count = count; this.count = count;
} }
private void reset() { private void reset() {
foundSectionCount = 0; foundSectionCount = 0;
currentIndex = start; currentIndex = start;
sectionStart = -1; sectionStart = -1;
closingToken = 0; closingToken = 0;
sectionEnd = -1; sectionEnd = -1;
removeCount = 0; removeCount = 0;
} }
private boolean findNextSectionStart() { private boolean findNextSectionStart() {
if (count >= 0 && foundSectionCount >= count) return false; if (count >= 0 && foundSectionCount >= count) return false;
while (currentIndex < args.length) { while (currentIndex < args.length) {
String arg = args[currentIndex]; String arg = args[currentIndex];
if (arg == null) { if (arg == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (arg.isEmpty()) { if (arg.isEmpty()) {
++currentIndex; ++currentIndex;
continue; continue;
} }
int openingTokenIndex = tokens.indexOf(arg.charAt(0)); int openingTokenIndex = tokens.indexOf(arg.charAt(0));
if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) { if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) {
++currentIndex; ++currentIndex;
continue; continue;
} }
// found // found
closingToken = tokens.charAt(openingTokenIndex | 1); closingToken = tokens.charAt(openingTokenIndex | 1);
sectionStart = currentIndex; sectionStart = currentIndex;
return true; return true;
} }
return false; return false;
} }
private boolean findNextSectionEnd() { private boolean findNextSectionEnd() {
while (currentIndex < args.length) { while (currentIndex < args.length) {
String arg = args[currentIndex]; String arg = args[currentIndex];
if (arg == null) { if (arg == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (arg.isEmpty() if (arg.isEmpty()
|| arg.charAt(arg.length() - 1) != closingToken || arg.charAt(arg.length() - 1) != closingToken
|| (sectionStart == currentIndex && arg.length() == 1)) { || (sectionStart == currentIndex && arg.length() == 1)) {
++currentIndex; ++currentIndex;
continue; continue;
} }
if (escapeChar != 0 if (escapeChar != 0
&& arg.length() > 1 && arg.length() > 1
&& arg.charAt(arg.length() - 2) == escapeChar) { && arg.charAt(arg.length() - 2) == escapeChar) {
// escaped // escaped
++currentIndex; ++currentIndex;
continue; continue;
} }
// found // found
closingToken = 0; closingToken = 0;
sectionEnd = currentIndex; sectionEnd = currentIndex;
++currentIndex; ++currentIndex;
return true; return true;
} }
return false; return false;
} }
private void processFoundSection() { private void processFoundSection() {
if (sectionStart == sectionEnd) { if (sectionStart == sectionEnd) {
String arg = args[sectionStart]; String arg = args[sectionStart];
args[sectionStart] = arg.substring(1, arg.length() - 1); args[sectionStart] = arg.substring(1, arg.length() - 1);
return; return;
} }
removeCount += sectionEnd - sectionStart; removeCount += sectionEnd - sectionStart;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(args[sectionStart].substring(1)); sb.append(args[sectionStart].substring(1));
for (int i = sectionStart + 1; i < sectionEnd; i++) { for (int i = sectionStart + 1; i < sectionEnd; i++) {
sb.append(' '); sb.append(' ');
sb.append(args[i]); sb.append(args[i]);
args[i] = null; args[i] = null;
} }
sb.append(' '); sb.append(' ');
sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1)); sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1));
args[sectionEnd] = null; args[sectionEnd] = null;
args[sectionStart] = sb.toString(); args[sectionStart] = sb.toString();
sectionStart = -1; sectionStart = -1;
sectionEnd = -1; sectionEnd = -1;
++foundSectionCount; ++foundSectionCount;
} }
String[] doProcess() { String[] doProcess() {
reset(); reset();
while (findNextSectionStart()) { while (findNextSectionStart()) {
if (findNextSectionEnd()) { if (findNextSectionEnd()) {
processFoundSection(); processFoundSection();
} else { } else {
currentIndex = sectionStart + 1; currentIndex = sectionStart + 1;
} }
} }
if (removeCount == 0) { if (removeCount == 0) {
return args; return args;
} }
String[] result = new String[args.length - removeCount]; String[] result = new String[args.length - removeCount];
int i = 0; int i = 0;
for (String arg : args) { for (String arg : args) {
if (arg != null) { if (arg != null) {
result[i++] = arg; result[i++] = arg;
} }
} }
return result; return result;
} }
void updateBuffer(ArgumentBuffer buffer) { void updateBuffer(ArgumentBuffer buffer) {
if (count < 0) { if (count < 0) {
buffer.setCursor(start); buffer.setCursor(start);
} else { } else {
buffer.setCursor(currentIndex); buffer.setCursor(currentIndex);
} }
} }
} }
} }

View File

@@ -89,6 +89,8 @@ public class ContextParser {
m_curRepeatingList = null; m_curRepeatingList = null;
assignDefaultValuesToUncomputedParams(); assignDefaultValuesToUncomputedParams();
arrayifyRepeatedParamValue(); arrayifyRepeatedParamValue();
m_done = true;
} }
} }

View File

@@ -1,41 +1,41 @@
package io.dico.dicore.command.parameter; package io.dico.dicore.command.parameter;
/** /**
* An interface to process tokens such as quotes * An interface to process tokens such as quotes
*/ */
@Deprecated @Deprecated
public interface IArgumentPreProcessor { public interface IArgumentPreProcessor {
/** /**
* Preprocess the arguments contained within the given ArgumentBuffer. * Preprocess the arguments contained within the given ArgumentBuffer.
* If no changes are made, this might return the same buffer. * If no changes are made, this might return the same buffer.
* Any arguments preceding {@code buffer.getCursor()} will not be affected. * Any arguments preceding {@code buffer.getCursor()} will not be affected.
* *
* <p> * <p>
* If {@code count} is non-negative, it declares a limit on the number of arguments after preprocessing. * If {@code count} is non-negative, it declares a limit on the number of arguments after preprocessing.
* In that case, the buffer's cursor is set to the index of the first argument following processed arguments. * In that case, the buffer's cursor is set to the index of the first argument following processed arguments.
* </p> * </p>
* *
* @param buffer the argument buffer * @param buffer the argument buffer
* @param count the maximum number of (processed) arguments * @param count the maximum number of (processed) arguments
* @return the arguments after preprocessing * @return the arguments after preprocessing
*/ */
ArgumentBuffer process(ArgumentBuffer buffer, int count); ArgumentBuffer process(ArgumentBuffer buffer, int count);
IArgumentPreProcessor NONE = (buffer, count) -> buffer; IArgumentPreProcessor NONE = (buffer, count) -> buffer;
/** /**
* Get an IArgumentPreProcessor that merges arguments between any two tokens * Get an IArgumentPreProcessor that merges arguments between any two tokens
* *
* @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs. * @param tokens The tokens that the merged arguments should be enclosed by, in subsequent pairs.
* Example: []{}"" * Example: []{}""
* This would mean the following would be merged: [ hello this is a merged argument] * This would mean the following would be merged: [ hello this is a merged argument]
* @param escapeChar the char that can be used to escape the given tokens * @param escapeChar the char that can be used to escape the given tokens
* @return The IArgumentPreProcessor * @return The IArgumentPreProcessor
*/ */
static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) { static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) {
return new ArgumentMergingPreProcessor(tokens, escapeChar); return new ArgumentMergingPreProcessor(tokens, escapeChar);
} }
} }

View File

@@ -1,148 +1,148 @@
package io.dico.dicore.command.parameter; package io.dico.dicore.command.parameter;
import java.util.*; import java.util.*;
/** /**
* IParameter definition for a command * IParameter definition for a command
*/ */
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public class ParameterList { public class ParameterList {
//private ParameterList parent; //private ParameterList parent;
private List<Parameter<?, ?>> indexedParameters; private List<Parameter<?, ?>> indexedParameters;
private Map<String, Parameter<?, ?>> byName; private Map<String, Parameter<?, ?>> byName;
//private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE; //private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE;
private int requiredCount = -1; private int requiredCount = -1;
private boolean repeatFinalParameter; private boolean repeatFinalParameter;
// if the final parameter is repeated and the command is implemented through reflection, // if the final parameter is repeated and the command is implemented through reflection,
// the repeated parameter is simply the last parameter of the method, rather than the last // the repeated parameter is simply the last parameter of the method, rather than the last
// indexed parameter. This might be a flag. As such, this field exists to ensure the correct // indexed parameter. This might be a flag. As such, this field exists to ensure the correct
// parameter is taken for repeating // parameter is taken for repeating
private boolean finalParameterMayBeFlag; private boolean finalParameterMayBeFlag;
/* /*
public ParameterList(ParameterList parent) { public ParameterList(ParameterList parent) {
this(); this();
if (parent.repeatFinalParameter) { if (parent.repeatFinalParameter) {
throw new IllegalArgumentException("Parent may not have repeating parameters"); throw new IllegalArgumentException("Parent may not have repeating parameters");
} }
this.parent = parent; this.parent = parent;
}*/ }*/
public ParameterList() { public ParameterList() {
this.indexedParameters = new ArrayList<>(); this.indexedParameters = new ArrayList<>();
this.byName = new LinkedHashMap<>(); this.byName = new LinkedHashMap<>();
this.repeatFinalParameter = false; this.repeatFinalParameter = false;
} }
/* /*
public IArgumentPreProcessor getArgumentPreProcessor() { public IArgumentPreProcessor getArgumentPreProcessor() {
return argumentPreProcessor; return argumentPreProcessor;
} }
public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) { public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) {
this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor; this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor;
return this; return this;
}*/ }*/
public boolean repeatFinalParameter() { public boolean repeatFinalParameter() {
return repeatFinalParameter; return repeatFinalParameter;
} }
public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) { public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) {
this.repeatFinalParameter = repeatFinalParameter; this.repeatFinalParameter = repeatFinalParameter;
return this; return this;
} }
public boolean finalParameterMayBeFlag() { public boolean finalParameterMayBeFlag() {
return finalParameterMayBeFlag; return finalParameterMayBeFlag;
} }
public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) { public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) {
this.finalParameterMayBeFlag = finalParameterMayBeFlag; this.finalParameterMayBeFlag = finalParameterMayBeFlag;
return this; return this;
} }
public int getRequiredCount() { public int getRequiredCount() {
return requiredCount == -1 ? indexedParameters.size() : requiredCount; return requiredCount == -1 ? indexedParameters.size() : requiredCount;
} }
public ParameterList setRequiredCount(int requiredCount) { public ParameterList setRequiredCount(int requiredCount) {
this.requiredCount = requiredCount; this.requiredCount = requiredCount;
return this; return this;
} }
public boolean hasAnyParameters() { public boolean hasAnyParameters() {
return !byName.isEmpty(); return !byName.isEmpty();
} }
public int getIndexedParameterCount() { public int getIndexedParameterCount() {
return indexedParameters.size(); return indexedParameters.size();
} }
public List<Parameter<?, ?>> getIndexedParameters() { public List<Parameter<?, ?>> getIndexedParameters() {
return Collections.unmodifiableList(indexedParameters); return Collections.unmodifiableList(indexedParameters);
} }
public Parameter<?, ?> getParameterByName(String name) { public Parameter<?, ?> getParameterByName(String name) {
return byName.get(name); return byName.get(name);
} }
public String getIndexedParameterName(int index) { public String getIndexedParameterName(int index) {
return indexedParameters.get(index).getName(); return indexedParameters.get(index).getName();
} }
public Map<String, Parameter<?, ?>> getParametersByName() { public Map<String, Parameter<?, ?>> getParametersByName() {
return Collections.unmodifiableMap(byName); return Collections.unmodifiableMap(byName);
} }
/** /**
* Add the given parameter to the end of this parameter list * Add the given parameter to the end of this parameter list
* Can be a flag * Can be a flag
* *
* @param parameter the parameter * @param parameter the parameter
* @return this * @return this
*/ */
public ParameterList addParameter(Parameter<?, ?> parameter) { public ParameterList addParameter(Parameter<?, ?> parameter) {
return addParameter(-1, parameter); return addParameter(-1, parameter);
} }
/** /**
* Add the given parameter to this parameter list * Add the given parameter to this parameter list
* If the parameter is a flag, the index is ignored * If the parameter is a flag, the index is ignored
* *
* @param index parameter index number, -1 if end * @param index parameter index number, -1 if end
* @param parameter the parameter * @param parameter the parameter
* @return this * @return this
* @throws NullPointerException if parameter is null * @throws NullPointerException if parameter is null
*/ */
public ParameterList addParameter(int index, Parameter<?, ?> parameter) { public ParameterList addParameter(int index, Parameter<?, ?> parameter) {
//System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag()); //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag());
byName.put(parameter.getName(), parameter); byName.put(parameter.getName(), parameter);
if (!parameter.isFlag()) { if (!parameter.isFlag()) {
indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter); indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter);
} }
return this; return this;
} }
public Parameter<?, ?> getRepeatedParameter() { public Parameter<?, ?> getRepeatedParameter() {
if (!repeatFinalParameter) { if (!repeatFinalParameter) {
return null; return null;
} }
if (finalParameterMayBeFlag) { if (finalParameterMayBeFlag) {
Iterator<Parameter<?, ?>> iterator = byName.values().iterator(); Iterator<Parameter<?, ?>> iterator = byName.values().iterator();
Parameter<?, ?> result = null; Parameter<?, ?> result = null;
while (iterator.hasNext()) { while (iterator.hasNext()) {
result = iterator.next(); result = iterator.next();
} }
return result; return result;
} }
if (indexedParameters.isEmpty()) { if (indexedParameters.isEmpty()) {
return null; return null;
} }
return indexedParameters.get(indexedParameters.size() - 1); return indexedParameters.get(indexedParameters.size() - 1);
} }
} }

View File

@@ -1,46 +1,46 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> { public class EnumParameterType<E extends Enum> extends SimpleParameterType<E, Void> {
private final E[] universe; private final E[] universe;
public EnumParameterType(Class<E> returnType) { public EnumParameterType(Class<E> returnType) {
super(returnType); super(returnType);
universe = returnType.getEnumConstants(); universe = returnType.getEnumConstants();
if (universe == null) { if (universe == null) {
throw new IllegalArgumentException("returnType must be an enum"); throw new IllegalArgumentException("returnType must be an enum");
} }
} }
@Override @Override
protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException { protected E parse(Parameter<E, Void> parameter, CommandSender sender, String input) throws CommandException {
for (E constant : universe) { for (E constant : universe) {
if (constant.name().equalsIgnoreCase(input)) { if (constant.name().equalsIgnoreCase(input)) {
return constant; return constant;
} }
} }
throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist"); throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist");
} }
@Override @Override
public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { public List<String> complete(Parameter<E, Void> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
String input = buffer.next().toUpperCase(); String input = buffer.next().toUpperCase();
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (E constant : universe) { for (E constant : universe) {
if (constant.name().toUpperCase().startsWith(input.toUpperCase())) { if (constant.name().toUpperCase().startsWith(input.toUpperCase())) {
result.add(constant.name().toLowerCase()); result.add(constant.name().toLowerCase());
} }
} }
return result; return result;
} }
} }

View File

@@ -1,44 +1,44 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
/** /**
* An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys} * An interface for an object that stores parameter types by {@link ParameterKey} and finds appropriate types for {@link ParameterKey parameterKeys}
*/ */
public interface IParameterTypeSelector { public interface IParameterTypeSelector {
<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key); <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key);
//<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key); //<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(ParameterKey key);
<TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key); <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key);
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType) {
return selectExact(returnType, null); return selectExact(returnType, null);
} }
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(Class<?> returnType, Class<? extends Annotation> annotationClass) {
return selectExact(new ParameterKey(returnType, annotationClass)); return selectExact(new ParameterKey(returnType, annotationClass));
} }
/* /*
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType) {
return selectExactOrSubclass(returnType, null); return selectExactOrSubclass(returnType, null);
} }
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExactOrSubclass(Class<?> returnType, Class<? extends Annotation> annotationClass) {
return selectExactOrSubclass(new ParameterKey(returnType, annotationClass)); return selectExactOrSubclass(new ParameterKey(returnType, annotationClass));
} }
*/ */
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType) {
return selectAny(returnType, null); return selectAny(returnType, null);
} }
default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) { default <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(Class<?> returnType, Class<? extends Annotation> annotationClass) {
return selectAny(new ParameterKey(returnType, annotationClass)); return selectAny(new ParameterKey(returnType, annotationClass));
} }
void addType(boolean infolessAlias, ParameterType<?, ?> type); void addType(boolean infolessAlias, ParameterType<?, ?> type);
} }

View File

@@ -1,114 +1,114 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Map based implementation of {@link IParameterTypeSelector} * Map based implementation of {@link IParameterTypeSelector}
*/ */
public class MapBasedParameterTypeSelector implements IParameterTypeSelector { public class MapBasedParameterTypeSelector implements IParameterTypeSelector {
static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false); static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false);
private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap; private final Map<ParameterKey, ParameterType<?, ?>> parameterTypeMap;
private final boolean useDefault; private final boolean useDefault;
public MapBasedParameterTypeSelector(boolean useDefault) { public MapBasedParameterTypeSelector(boolean useDefault) {
this.parameterTypeMap = new HashMap<>(); this.parameterTypeMap = new HashMap<>();
this.useDefault = useDefault; this.useDefault = useDefault;
} }
@Override @Override
public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) { public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectExact(ParameterKey key) {
ParameterType<?, ?> out = parameterTypeMap.get(key); ParameterType<?, ?> out = parameterTypeMap.get(key);
if (useDefault && out == null) { if (useDefault && out == null) {
out = defaultSelector.selectExact(key); out = defaultSelector.selectExact(key);
} }
if (out == null && key.getReturnType().isEnum()) { if (out == null && key.getReturnType().isEnum()) {
//noinspection unchecked //noinspection unchecked
out = new EnumParameterType(key.getReturnType()); out = new EnumParameterType(key.getReturnType());
addType(false, out); addType(false, out);
} }
return cast(out); return cast(out);
} }
@Override @Override
public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) { public <TReturn, TParamInfo> ParameterType<TReturn, TParamInfo> selectAny(ParameterKey key) {
ParameterType<TReturn, TParamInfo> exact = selectExact(key); ParameterType<TReturn, TParamInfo> exact = selectExact(key);
if (exact != null) { if (exact != null) {
return exact; return exact;
} }
if (key.getAnnotationClass() != null) { if (key.getAnnotationClass() != null) {
exact = selectExact(new ParameterKey(key.getReturnType())); exact = selectExact(new ParameterKey(key.getReturnType()));
if (exact != null) { if (exact != null) {
return exact; return exact;
} }
} }
Class<?> returnType = key.getReturnType(); Class<?> returnType = key.getReturnType();
Class<? extends Annotation> annotationClass = key.getAnnotationClass(); Class<? extends Annotation> annotationClass = key.getAnnotationClass();
ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false); ParameterType<?, ?> out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false);
if (out == null && useDefault) { if (out == null && useDefault) {
out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false); out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false);
} }
if (out == null) { if (out == null) {
out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true); out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true);
} }
if (out == null && useDefault) { if (out == null && useDefault) {
out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true); out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true);
} }
return cast(out); return cast(out);
} }
private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType, private static ParameterType<?, ?> selectByReturnType(Map<ParameterKey, ParameterType<?, ?>> map, Class<?> returnType,
Class<? extends Annotation> annotationClass, boolean allowSubclass) { Class<? extends Annotation> annotationClass, boolean allowSubclass) {
ParameterType<?, ?> out = null; ParameterType<?, ?> out = null;
if (allowSubclass) { if (allowSubclass) {
for (ParameterType<?, ?> type : map.values()) { for (ParameterType<?, ?> type : map.values()) {
if (returnType.isAssignableFrom(type.getReturnType())) { if (returnType.isAssignableFrom(type.getReturnType())) {
if (annotationClass == type.getAnnotationClass()) { if (annotationClass == type.getAnnotationClass()) {
out = type; out = type;
break; break;
} }
if (out == null) { if (out == null) {
out = type; out = type;
} }
} }
} }
} else { } else {
for (ParameterType<?, ?> type : map.values()) { for (ParameterType<?, ?> type : map.values()) {
if (returnType == type.getReturnType()) { if (returnType == type.getReturnType()) {
if (annotationClass == type.getAnnotationClass()) { if (annotationClass == type.getAnnotationClass()) {
out = type; out = type;
break; break;
} }
if (out == null) { if (out == null) {
out = type; out = type;
} }
} }
} }
} }
return out; return out;
} }
private static <T> T cast(Object o) { private static <T> T cast(Object o) {
//noinspection unchecked //noinspection unchecked
return (T) o; return (T) o;
} }
@Override @Override
public void addType(boolean infolessAlias, ParameterType<?, ?> type) { public void addType(boolean infolessAlias, ParameterType<?, ?> type) {
parameterTypeMap.put(type.getTypeKey(), type); parameterTypeMap.put(type.getTypeKey(), type);
if (infolessAlias) { if (infolessAlias) {
parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type); parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type);
} }
} }
static { static {
// registers default parameter types // registers default parameter types
ParameterTypes.clinit(); ParameterTypes.clinit();
} }
} }

View File

@@ -1,80 +1,80 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import io.dico.dicore.Reflection; import io.dico.dicore.Reflection;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
/** /**
* This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters). * This class serves the purpose of having annotated parameter configurations (such as ranges for number parameters).
* Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information * Such configurations must be possible to obtain without using annotations, and as such, there should be a class conveying the information
* that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information. * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information.
* *
* @param <TAnnotation> the annotation type for parameters * @param <TAnnotation> the annotation type for parameters
* @param <TParamInfo> the object type that holds the information required in memory * @param <TParamInfo> the object type that holds the information required in memory
*/ */
public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> { public abstract class ParameterConfig<TAnnotation extends Annotation, TParamInfo> implements Comparable<ParameterConfig<?, ?>> {
private final Class<TAnnotation> annotationClass; private final Class<TAnnotation> annotationClass;
// protected final TParamInfo defaultValue; // protected final TParamInfo defaultValue;
public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) { public ParameterConfig(Class<TAnnotation> annotationClass/*, TParamInfo defaultValue*/) {
this.annotationClass = annotationClass; this.annotationClass = annotationClass;
//this.defaultValue = defaultValue; //this.defaultValue = defaultValue;
} }
public final Class<TAnnotation> getAnnotationClass() { public final Class<TAnnotation> getAnnotationClass() {
return annotationClass; return annotationClass;
} }
/* /*
public TParamInfo getDefaultValue() { public TParamInfo getDefaultValue() {
return defaultValue; return defaultValue;
}*/ }*/
protected abstract TParamInfo toParameterInfo(TAnnotation annotation); protected abstract TParamInfo toParameterInfo(TAnnotation annotation);
public TParamInfo getParameterInfo(Annotation annotation) { public TParamInfo getParameterInfo(Annotation annotation) {
//noinspection unchecked //noinspection unchecked
return toParameterInfo((TAnnotation) annotation); return toParameterInfo((TAnnotation) annotation);
} }
public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo>
includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) { includeMemoryClass(Class<TAnnotation> annotationClass, Class<TParamInfo> memoryClass) {
Constructor<TParamInfo> constructor; Constructor<TParamInfo> constructor;
//TParamInfo defaultValue; //TParamInfo defaultValue;
try { try {
constructor = memoryClass.getConstructor(annotationClass); constructor = memoryClass.getConstructor(annotationClass);
//defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT"); //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT");
} catch (NoSuchMethodException | IllegalArgumentException ex) { } catch (NoSuchMethodException | IllegalArgumentException ex) {
throw new IllegalArgumentException(ex); throw new IllegalArgumentException(ex);
} }
/* /*
if (defaultValue == null) try { if (defaultValue == null) try {
defaultValue = memoryClass.newInstance(); defaultValue = memoryClass.newInstance();
} catch (IllegalAccessException | InstantiationException ex) { } catch (IllegalAccessException | InstantiationException ex) {
throw new IllegalArgumentException("Failed to get a default value for the param info", ex); throw new IllegalArgumentException("Failed to get a default value for the param info", ex);
}*/ }*/
return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) { return new ParameterConfig<TAnnotation, TParamInfo>(annotationClass/*, defaultValue*/) {
@Override @Override
public TParamInfo toParameterInfo(TAnnotation annotation) { public TParamInfo toParameterInfo(TAnnotation annotation) {
try { try {
return constructor.newInstance(annotation); return constructor.newInstance(annotation);
} catch (Exception ex) { } catch (Exception ex) {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
}; };
} }
public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) { public static <TAnnotation extends Annotation, TParamInfo> ParameterConfig<TAnnotation, TParamInfo> getMemoryClassFromField(Class<TAnnotation> annotationClass) {
return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS")); return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS"));
} }
@Override @Override
public int compareTo(ParameterConfig<?, ?> o) { public int compareTo(ParameterConfig<?, ?> o) {
return 0; return 0;
} }
} }

View File

@@ -1,149 +1,149 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import io.dico.dicore.Reflection; import io.dico.dicore.Reflection;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.annotation.Range; import io.dico.dicore.command.annotation.Range;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
* A parameter type. * A parameter type.
* Takes care of parsing, default values as well as completions. * Takes care of parsing, default values as well as completions.
* *
* @param <TReturn> type of the parameter * @param <TReturn> type of the parameter
* @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory} * @param <TParamInfo> the info object type for the parameter (Example: {@link Range.Memory}
*/ */
public abstract class ParameterType<TReturn, TParamInfo> { public abstract class ParameterType<TReturn, TParamInfo> {
private final Class<TReturn> returnType; private final Class<TReturn> returnType;
private final ParameterConfig<?, TParamInfo> parameterConfig; private final ParameterConfig<?, TParamInfo> parameterConfig;
protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current protected final ParameterType<TReturn, TParamInfo> otherType; // flag or non-flag, depending on current
public ParameterType(Class<TReturn> returnType) { public ParameterType(Class<TReturn> returnType) {
this(returnType, null); this(returnType, null);
} }
public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) { public ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
this.returnType = Objects.requireNonNull(returnType); this.returnType = Objects.requireNonNull(returnType);
this.parameterConfig = paramConfig; this.parameterConfig = paramConfig;
ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter(); ParameterType<TReturn, TParamInfo> otherType = flagTypeParameter();
this.otherType = otherType == null ? this : otherType; this.otherType = otherType == null ? this : otherType;
} }
protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) { protected ParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> parameterConfig, ParameterType<TReturn, TParamInfo> otherType) {
this.returnType = returnType; this.returnType = returnType;
this.parameterConfig = parameterConfig; this.parameterConfig = parameterConfig;
this.otherType = otherType; this.otherType = otherType;
} }
public int getExpectedAmountOfConsumedArguments() { public int getExpectedAmountOfConsumedArguments() {
return 1; return 1;
} }
public boolean canBeFlag() { public boolean canBeFlag() {
return this == otherType; return this == otherType;
} }
public boolean isFlagExplicitly() { public boolean isFlagExplicitly() {
return this instanceof FlagParameterType; return this instanceof FlagParameterType;
} }
/** /**
* @return The return type * @return The return type
*/ */
public final Class<TReturn> getReturnType() { public final Class<TReturn> getReturnType() {
return returnType; return returnType;
} }
public final Class<?> getAnnotationClass() { public final Class<?> getAnnotationClass() {
return parameterConfig == null ? null : parameterConfig.getAnnotationClass(); return parameterConfig == null ? null : parameterConfig.getAnnotationClass();
} }
public final ParameterConfig<?, TParamInfo> getParameterConfig() { public final ParameterConfig<?, TParamInfo> getParameterConfig() {
return parameterConfig; return parameterConfig;
} }
public ParameterKey getTypeKey() { public ParameterKey getTypeKey() {
return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null); return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null);
} }
public ParameterKey getInfolessTypeKey() { public ParameterKey getInfolessTypeKey() {
return new ParameterKey(returnType, null); return new ParameterKey(returnType, null);
} }
protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() { protected FlagParameterType<TReturn, TParamInfo> flagTypeParameter() {
return null; return null;
} }
public ParameterType<TReturn, TParamInfo> asFlagParameter() { public ParameterType<TReturn, TParamInfo> asFlagParameter() {
return canBeFlag() ? this : otherType; return canBeFlag() ? this : otherType;
} }
public ParameterType<TReturn, TParamInfo> asNormalParameter() { public ParameterType<TReturn, TParamInfo> asNormalParameter() {
return isFlagExplicitly() ? otherType : this; return isFlagExplicitly() ? otherType : this;
} }
public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException; public abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException;
public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { public TReturn parseForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
return parse(parameter, context.getSender(), buffer); return parse(parameter, context.getSender(), buffer);
} }
public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { public TReturn getDefaultValue(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
return null; return null;
} }
public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { public TReturn getDefaultValueForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
return getDefaultValue(parameter, context.getSender(), buffer); return getDefaultValue(parameter, context.getSender(), buffer);
} }
public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { public List<String> complete(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, Location location, ArgumentBuffer buffer) {
return Collections.emptyList(); return Collections.emptyList();
} }
public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) { public List<String> completeForContext(Parameter<TReturn, TParamInfo> parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) {
return complete(parameter, context.getSender(), location, buffer); return complete(parameter, context.getSender(), location, buffer);
} }
protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> { protected static abstract class FlagParameterType<TResult, TParamInfo> extends ParameterType<TResult, TParamInfo> {
protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) { protected FlagParameterType(ParameterType<TResult, TParamInfo> otherType) {
super(otherType.returnType, otherType.parameterConfig, otherType); super(otherType.returnType, otherType.parameterConfig, otherType);
} }
@Override @Override
public int getExpectedAmountOfConsumedArguments() { public int getExpectedAmountOfConsumedArguments() {
return otherType.getExpectedAmountOfConsumedArguments(); return otherType.getExpectedAmountOfConsumedArguments();
} }
@Override @Override
public boolean canBeFlag() { public boolean canBeFlag() {
return true; return true;
} }
@Override @Override
protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() { protected final FlagParameterType<TResult, TParamInfo> flagTypeParameter() {
return this; return this;
} }
@Override @Override
public ParameterType<TResult, TParamInfo> asFlagParameter() { public ParameterType<TResult, TParamInfo> asFlagParameter() {
return this; return this;
} }
@Override @Override
public ParameterType<TResult, TParamInfo> asNormalParameter() { public ParameterType<TResult, TParamInfo> asNormalParameter() {
return otherType; return otherType;
} }
} }
} }

View File

@@ -1,31 +1,31 @@
package io.dico.dicore.command.parameter.type; package io.dico.dicore.command.parameter.type;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
/** /**
* An abstraction for parameter types that only parse a single argument * An abstraction for parameter types that only parse a single argument
* *
* @param <TReturn> the parameter type * @param <TReturn> the parameter type
* @param <TParamInfo> parameter info object type * @param <TParamInfo> parameter info object type
*/ */
public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> { public abstract class SimpleParameterType<TReturn, TParamInfo> extends ParameterType<TReturn, TParamInfo> {
public SimpleParameterType(Class<TReturn> returnType) { public SimpleParameterType(Class<TReturn> returnType) {
super(returnType); super(returnType);
} }
public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) { public SimpleParameterType(Class<TReturn> returnType, ParameterConfig<?, TParamInfo> paramConfig) {
super(returnType, paramConfig); super(returnType, paramConfig);
} }
protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException; protected abstract TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, String input) throws CommandException;
@Override @Override
public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { public TReturn parse(Parameter<TReturn, TParamInfo> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
return parse(parameter, sender, buffer.requireNext(parameter.getName())); return parse(parameter, sender, buffer.requireNext(parameter.getName()));
} }
} }

View File

@@ -1,56 +1,56 @@
package io.dico.dicore.command.predef; package io.dico.dicore.command.predef;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.command.IContextFilter; import io.dico.dicore.command.IContextFilter;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> { public class DefaultGroupCommand extends PredefinedCommand<DefaultGroupCommand> {
private static final DefaultGroupCommand instance; private static final DefaultGroupCommand instance;
private static final IContextFilter noArgumentFilter; private static final IContextFilter noArgumentFilter;
public static DefaultGroupCommand getInstance() { public static DefaultGroupCommand getInstance() {
return instance; return instance;
} }
private DefaultGroupCommand(boolean modifiable) { private DefaultGroupCommand(boolean modifiable) {
addContextFilter(IContextFilter.INHERIT_PERMISSIONS); addContextFilter(IContextFilter.INHERIT_PERMISSIONS);
addContextFilter(noArgumentFilter); addContextFilter(noArgumentFilter);
this.modifiable = modifiable; this.modifiable = modifiable;
} }
public DefaultGroupCommand() { public DefaultGroupCommand() {
this(true); this(true);
} }
@Override @Override
protected DefaultGroupCommand newModifiableInstance() { protected DefaultGroupCommand newModifiableInstance() {
return new DefaultGroupCommand(true); return new DefaultGroupCommand(true);
} }
@Override @Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException { public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1); context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1);
return null; return null;
} }
static { static {
noArgumentFilter = new IContextFilter() { noArgumentFilter = new IContextFilter() {
@Override @Override
public void filterContext(ExecutionContext context) throws CommandException { public void filterContext(ExecutionContext context) throws CommandException {
if (context.getBuffer().hasNext()) { if (context.getBuffer().hasNext()) {
throw new CommandException("No such command: /" + context.getAddress().getAddress() throw new CommandException("No such command: /" + context.getAddress().getAddress()
+ " " + context.getBuffer().next()); + " " + context.getBuffer().next());
} }
} }
@Override @Override
public Priority getPriority() { public Priority getPriority() {
return Priority.EARLY; return Priority.EARLY;
} }
}; };
instance = new DefaultGroupCommand(false); instance = new DefaultGroupCommand(false);
} }
} }

View File

@@ -1,76 +1,76 @@
package io.dico.dicore.command.predef; package io.dico.dicore.command.predef;
import io.dico.dicore.command.*; import io.dico.dicore.command.*;
import io.dico.dicore.command.annotation.Range; import io.dico.dicore.command.annotation.Range;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.type.NumberParameterType; import io.dico.dicore.command.parameter.type.NumberParameterType;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
/** /**
* The help command * The help command
*/ */
public class HelpCommand extends PredefinedCommand<HelpCommand> { public class HelpCommand extends PredefinedCommand<HelpCommand> {
private static final Parameter<Integer, Range.Memory> pageParameter; private static final Parameter<Integer, Range.Memory> pageParameter;
public static final HelpCommand INSTANCE; public static final HelpCommand INSTANCE;
private HelpCommand(boolean modifiable) { private HelpCommand(boolean modifiable) {
super(modifiable); super(modifiable);
getParameterList().addParameter(pageParameter); getParameterList().addParameter(pageParameter);
getParameterList().setRequiredCount(0); getParameterList().setRequiredCount(0);
setDescription("Shows this help page"); setDescription("Shows this help page");
} }
@Override @Override
protected HelpCommand newModifiableInstance() { protected HelpCommand newModifiableInstance() {
return new HelpCommand(true); return new HelpCommand(true);
} }
@Override @Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException { public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
ICommandAddress target = context.getAddress(); ICommandAddress target = context.getAddress();
if (context.getAddress().getCommand() == this) { if (context.getAddress().getCommand() == this) {
target = target.getParent(); target = target.getParent();
} }
context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1); context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.<Integer>get("page") - 1);
return null; return null;
} }
public static void registerAsChild(ICommandAddress address) { public static void registerAsChild(ICommandAddress address) {
registerAsChild(address, "help"); registerAsChild(address, "help");
} }
public static void registerAsChild(ICommandAddress address, String main, String... aliases) { public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
} }
static { static {
pageParameter = new Parameter<>("page", "the page number", pageParameter = new Parameter<>("page", "the page number",
new NumberParameterType<Integer>(Integer.TYPE) { new NumberParameterType<Integer>(Integer.TYPE) {
@Override @Override
protected Integer parse(String input) throws NumberFormatException { protected Integer parse(String input) throws NumberFormatException {
return Integer.parseInt(input); return Integer.parseInt(input);
} }
@Override @Override
protected Integer select(Number number) { protected Integer select(Number number) {
return number.intValue(); return number.intValue();
} }
@Override @Override
public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { public Integer parseForContext(Parameter<Integer, Range.Memory> parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException {
if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) { if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) {
// An address was executed with its help command as target // An address was executed with its help command as target
buffer.next(); buffer.next();
return 1; return 1;
} }
return parse(parameter, context.getSender(), buffer); return parse(parameter, context.getSender(), buffer);
} }
}, },
new Range.Memory(1, Integer.MAX_VALUE, 1)); new Range.Memory(1, Integer.MAX_VALUE, 1));
INSTANCE = new HelpCommand(false); INSTANCE = new HelpCommand(false);
} }
} }

View File

@@ -1,50 +1,50 @@
package io.dico.dicore.command.predef; package io.dico.dicore.command.predef;
import io.dico.dicore.command.CommandBuilder; import io.dico.dicore.command.CommandBuilder;
import io.dico.dicore.command.ExtendedCommand; import io.dico.dicore.command.ExtendedCommand;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
* Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder} * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder}
*/ */
public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> { public abstract class PredefinedCommand<T extends PredefinedCommand<T>> extends ExtendedCommand<T> {
static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>(); static final Map<String, Consumer<ICommandAddress>> predefinedCommandGenerators = new HashMap<>();
/** /**
* Get a predefined command * Get a predefined command
* *
* @param name the name * @param name the name
* @return the subscriber * @return the subscriber
*/ */
public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) { public static Consumer<ICommandAddress> getPredefinedCommandGenerator(String name) {
return predefinedCommandGenerators.get(name); return predefinedCommandGenerators.get(name);
} }
/** /**
* Register a predefined command * Register a predefined command
* *
* @param name the name * @param name the name
* @param consumer the generator which adds the child to the address * @param consumer the generator which adds the child to the address
* @return true if and only if the subscriber was registered (false if the name exists) * @return true if and only if the subscriber was registered (false if the name exists)
*/ */
public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) { public static boolean registerPredefinedCommandGenerator(String name, Consumer<ICommandAddress> consumer) {
return predefinedCommandGenerators.putIfAbsent(name, consumer) == null; return predefinedCommandGenerators.putIfAbsent(name, consumer) == null;
} }
static { static {
registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild); registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild);
//noinspection StaticInitializerReferencesSubClass //noinspection StaticInitializerReferencesSubClass
registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild); registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild);
} }
public PredefinedCommand() { public PredefinedCommand() {
} }
public PredefinedCommand(boolean modifiable) { public PredefinedCommand(boolean modifiable) {
super(modifiable); super(modifiable);
} }
} }

View File

@@ -1,36 +1,36 @@
package io.dico.dicore.command.predef; package io.dico.dicore.command.predef;
import io.dico.dicore.command.*; import io.dico.dicore.command.*;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
/** /**
* The syntax command * The syntax command
*/ */
public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> { public class SyntaxCommand extends PredefinedCommand<SyntaxCommand> {
public static final SyntaxCommand INSTANCE = new SyntaxCommand(false); public static final SyntaxCommand INSTANCE = new SyntaxCommand(false);
private SyntaxCommand(boolean modifiable) { private SyntaxCommand(boolean modifiable) {
super(modifiable); super(modifiable);
setDescription("Describes how to use the command"); setDescription("Describes how to use the command");
} }
@Override @Override
protected SyntaxCommand newModifiableInstance() { protected SyntaxCommand newModifiableInstance() {
return new SyntaxCommand(true); return new SyntaxCommand(true);
} }
@Override @Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException { public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent()); context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent());
return null; return null;
} }
public static void registerAsChild(ICommandAddress address) { public static void registerAsChild(ICommandAddress address) {
registerAsChild(address, "syntax"); registerAsChild(address, "syntax");
} }
public static void registerAsChild(ICommandAddress address, String main, String... aliases) { public static void registerAsChild(ICommandAddress address, String main, String... aliases) {
((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases));
} }
} }

View File

@@ -1,122 +1,122 @@
package io.dico.dicore.command.registration; package io.dico.dicore.command.registration;
import io.dico.dicore.command.ICommandAddress; import io.dico.dicore.command.ICommandAddress;
import io.dico.dicore.command.ICommandDispatcher; import io.dico.dicore.command.ICommandDispatcher;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List; import java.util.List;
/** /**
* This class extends the bukkit's command class. * This class extends the bukkit's command class.
* Instances are injected into the command map. * Instances are injected into the command map.
*/ */
public class BukkitCommand extends Command { public class BukkitCommand extends Command {
private ICommandDispatcher dispatcher; private ICommandDispatcher dispatcher;
private ICommandAddress origin; private ICommandAddress origin;
public BukkitCommand(ICommandAddress address) { public BukkitCommand(ICommandAddress address) {
super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size())); super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size()));
this.dispatcher = address.getDispatcherForTree(); this.dispatcher = address.getDispatcherForTree();
this.origin = address; this.origin = address;
setTimingsIfNecessary(this); setTimingsIfNecessary(this);
} }
private static ICommandAddress validateTree(ICommandAddress tree) { private static ICommandAddress validateTree(ICommandAddress tree) {
if (!tree.hasParent()) { if (!tree.hasParent()) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (tree.getNames().isEmpty()) { if (tree.getNames().isEmpty()) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return tree; return tree;
} }
public ICommandAddress getOrigin() { public ICommandAddress getOrigin() {
return origin; return origin;
} }
@Override @Override
public boolean execute(CommandSender sender, String label, String[] args) { public boolean execute(CommandSender sender, String label, String[] args) {
if (!dispatcher.dispatchCommand(sender, label, args)) { if (!dispatcher.dispatchCommand(sender, label, args)) {
//System.out.println("failed to dispatch command"); //System.out.println("failed to dispatch command");
// target command not found, send a message in the future TODO // target command not found, send a message in the future TODO
} }
return true; return true;
} }
@Override @Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
return this.tabComplete(sender, alias, args, null); return this.tabComplete(sender, alias, args, null);
} }
//@Override //@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
return dispatcher.getTabCompletions(sender, alias, location, args); return dispatcher.getTabCompletions(sender, alias, location, args);
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
BukkitCommand that = (BukkitCommand) o; BukkitCommand that = (BukkitCommand) o;
return getName().equals(that.getName()) && dispatcher == that.dispatcher; return getName().equals(that.getName()) && dispatcher == that.dispatcher;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return dispatcher.hashCode() | getName().hashCode(); return dispatcher.hashCode() | getName().hashCode();
} }
private static void setTimingsIfNecessary(Command object) { private static void setTimingsIfNecessary(Command object) {
// with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system // with paper spigot, the timings are not set by super constructor but by CommandMap.register(), which is not invoked for this system
// I use reflection so that the project does not require paper spigot to build // I use reflection so that the project does not require paper spigot to build
try { try {
// public field // public field
Field field = Command.class.getDeclaredField("timings"); Field field = Command.class.getDeclaredField("timings");
if (field.get(object) != null) return; if (field.get(object) != null) return;
Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager"); Class<?> clazz = Class.forName("co.aikar.timings.TimingsManager");
// public method // public method
Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class); Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class);
Object timings = method.invoke(null, "", object); Object timings = method.invoke(null, "", object);
field.set(object, timings); field.set(object, timings);
} catch (Throwable ignored) { } catch (Throwable ignored) {
} }
} }
/* /*
public static void registerToMap(ICommandAddress tree, Map<String, Command> map) { public static void registerToMap(ICommandAddress tree, Map<String, Command> map) {
BukkitCommand command = new BukkitCommand(tree); BukkitCommand command = new BukkitCommand(tree);
Iterator<String> iterator = tree.getNames().iterator(); Iterator<String> iterator = tree.getNames().iterator();
map.put(iterator.next(), command); map.put(iterator.next(), command);
while (iterator.hasNext()) { while (iterator.hasNext()) {
map.putIfAbsent(iterator.next(), command); map.putIfAbsent(iterator.next(), command);
} }
} }
public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) { public static void unregisterFromMap(ICommandAddress tree, Map<String, Command> map) {
map.values().remove(new BukkitCommand(tree)); map.values().remove(new BukkitCommand(tree));
} }
public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) { public static void registerChildrenToMap(ICommandAddress tree, Map<String, Command> map) {
for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) { for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
ICommandAddress child = entry.getValue(); ICommandAddress child = entry.getValue();
registerToMap(child, map); registerToMap(child, map);
} }
} }
public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) { public static void unregisterChildenFromMap(ICommandAddress tree, Map<String, Command> map) {
for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) { for (Map.Entry<String, ? extends ICommandAddress> entry : tree.getChildren().entrySet()) {
ICommandAddress child = entry.getValue(); ICommandAddress child = entry.getValue();
unregisterFromMap(child, map); unregisterFromMap(child, map);
} }
} }
*/ */
} }

View File

@@ -1,59 +1,59 @@
package io.dico.dicore.command.registration; package io.dico.dicore.command.registration;
import io.dico.dicore.Reflection; import io.dico.dicore.Reflection;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.SimpleCommandMap; import org.bukkit.command.SimpleCommandMap;
import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.SimplePluginManager;
import java.util.*; import java.util.*;
/** /**
* Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map. * Provides access to bukkit's {@code Map<String, org.bukkit.command.Command>} command map.
*/ */
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public class CommandMap { public class CommandMap {
private static final Map<String, Command> commandMap = findCommandMap(); private static final Map<String, Command> commandMap = findCommandMap();
private CommandMap() { private CommandMap() {
} }
public static Map<String, Command> getCommandMap() { public static Map<String, Command> getCommandMap() {
return Objects.requireNonNull(commandMap); return Objects.requireNonNull(commandMap);
} }
public static boolean isAvailable() { public static boolean isAvailable() {
return commandMap != null; return commandMap != null;
} }
public static Command get(String key) { public static Command get(String key) {
return commandMap.get(key); return commandMap.get(key);
} }
public static void put(String key, Command command) { public static void put(String key, Command command) {
commandMap.put(key, command); commandMap.put(key, command);
} }
public static Collection<String> replace(Command command, Command replacement) { public static Collection<String> replace(Command command, Command replacement) {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
for (Map.Entry<String, Command> entry : commandMap.entrySet()) { for (Map.Entry<String, Command> entry : commandMap.entrySet()) {
if (entry.getValue() == command) { if (entry.getValue() == command) {
entry.setValue(replacement); entry.setValue(replacement);
result.add(entry.getKey()); result.add(entry.getKey());
} }
} }
return result; return result;
} }
private static Map<String, Command> findCommandMap() { private static Map<String, Command> findCommandMap() {
try { try {
return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands", return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands",
Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager())); Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager()));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(); ex.printStackTrace();
return null; return null;
} }
} }
} }

View File

@@ -1,27 +1,27 @@
package io.dico.dicore.command.registration.reflect; package io.dico.dicore.command.registration.reflect;
/** /**
* Thrown if an error occurs while 'parsing' a reflection command method * Thrown if an error occurs while 'parsing' a reflection command method
* Other errors can be thrown too in there that may not be directly relevant to a parsing error. * Other errors can be thrown too in there that may not be directly relevant to a parsing error.
*/ */
public class CommandParseException extends Exception { public class CommandParseException extends Exception {
public CommandParseException() { public CommandParseException() {
} }
public CommandParseException(String message) { public CommandParseException(String message) {
super(message); super(message);
} }
public CommandParseException(String message, Throwable cause) { public CommandParseException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
public CommandParseException(Throwable cause) { public CommandParseException(Throwable cause) {
super(cause); super(cause);
} }
public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace); super(message, cause, enableSuppression, writableStackTrace);
} }
} }

View File

@@ -1,36 +1,36 @@
package io.dico.dicore.command.registration.reflect; package io.dico.dicore.command.registration.reflect;
import io.dico.dicore.command.ExecutionContext; import io.dico.dicore.command.ExecutionContext;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public interface ICommandInterceptor { public interface ICommandInterceptor {
/** /**
* Get the receiver of the command, if applicable. * Get the receiver of the command, if applicable.
* A command has a receiver if its first parameter implements {@link ICommandReceiver} * A command has a receiver if its first parameter implements {@link ICommandReceiver}
* and its instance object implements this interface. * and its instance object implements this interface.
* *
* @param context the context of execution * @param context the context of execution
* @param target the method of the command * @param target the method of the command
* @param cmdName the name of the command * @param cmdName the name of the command
* @return the receiver * @return the receiver
*/ */
default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) { default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) {
return null; return null;
} }
/** /**
* If applicable, get the coroutine context to use in suspend functions (Kotlin only). * If applicable, get the coroutine context to use in suspend functions (Kotlin only).
* The return type is object to avoid depending on the kotlin runtime. * The return type is object to avoid depending on the kotlin runtime.
* *
* @param context the context of execution * @param context the context of execution
* @param target the method of the command * @param target the method of the command
* @param cmdName the name of the command * @param cmdName the name of the command
* @return the coroutine context * @return the coroutine context
*/ */
default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) { default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) {
return null; return null;
} }
} }

View File

@@ -1,5 +1,5 @@
package io.dico.dicore.command.registration.reflect; package io.dico.dicore.command.registration.reflect;
public interface ICommandReceiver { public interface ICommandReceiver {
} }

View File

@@ -0,0 +1,186 @@
package io.dico.dicore.command.registration.reflect;
import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.ExecutionContext;
import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
/**
* Call flags store which extra parameters the target function expects on top of command parameters.
* All 4 possible extra parameters are listed below.
* <p>
* Extra parameters are ordered by the bit that represents them in the call flags.
* They can either be leading or trailing the command's parameters.
*/
public class ReflectiveCallFlags {
/**
* Receiver ({@code this} in some kotlin functions - always first parameter)
*
* @see ICommandInterceptor#getReceiver(io.dico.dicore.command.ExecutionContext, java.lang.reflect.Method, String)
*/
public static final int RECEIVER_BIT = 1 << 0;
/**
* CommandSender
*
* @see org.bukkit.command.CommandSender
*/
public static final int SENDER_BIT = 1 << 1;
/**
* ExecutionContext
*
* @see io.dico.dicore.command.ExecutionContext
*/
public static final int CONTEXT_BIT = 1 << 2;
/**
* Continuation (trailing parameters of kotlin suspended functions)
*
* @see kotlin.coroutines.Continuation
*/
public static final int CONTINUATION_BIT = 1 << 3;
/**
* Mask of extra parameters that trail the command's parameters, instead of leading.
*/
public static final int TRAILING_MASK = CONTINUATION_BIT;
/**
* Check if the call arg is trailing the command's parameters.
*
* @param bit the bit used for the call flag
* @return true if the call arg is trailing the command's parameters
*/
public static boolean isTrailingCallArg(int bit) {
return (bit & TRAILING_MASK) != 0;
}
/**
* Number of call arguments leading the command parameters.
*
* @param flags the call flags
* @return the number of call arguments leading the command parameters
*/
public static int getLeadingCallArgNum(int flags) {
return Integer.bitCount(flags & ~TRAILING_MASK);
}
/**
* Number of call arguments trailing the command parameters.
*
* @param flags the call flags
* @return the number of call arguments trailing the command parameters
*/
public static int getTrailingCallArgNum(int flags) {
return Integer.bitCount(flags & TRAILING_MASK);
}
/**
* Check if the flags contain the call arg.
*
* @param flags the call flags
* @param bit the bit used for the call flag
* @return true if the flags contain the call arg
*/
public static boolean hasCallArg(int flags, int bit) {
return (flags & bit) != 0;
}
/**
* Get the index used for the call arg when calling the reflective function
*
* @param flags the call flags
* @param bit the bit used for the call flag
* @param cmdParameterNum the number of parameters of the command
* @return the index used for the call arg
*/
public static int getCallArgIndex(int flags, int bit, int cmdParameterNum) {
if ((bit & TRAILING_MASK) == 0) {
// Leading.
int preceding = precedingMaskFrom(bit);
int mask = flags & precedingMaskFrom(bit) & ~TRAILING_MASK;
// Count the number of present call args that are leading and precede the given bit
return Integer.bitCount(flags & precedingMaskFrom(bit) & ~TRAILING_MASK);
} else {
// Trailing.
// Count the number of present call args that are leading
// plus the number of present call args that are trailing and precede the given bit
// plus the command's parameters
return Integer.bitCount(flags & ~TRAILING_MASK)
+ Integer.bitCount(flags & precedingMaskFrom(bit) & TRAILING_MASK)
+ cmdParameterNum;
}
}
/**
* Get the mask for all bits trailing the given fromBit
*
* <p>
* For example, if the bit is 00010000
* This function returns 00001111
* <p>
*
* @param fromBit number with the bit set there the ones should stop.
* @return the mask for all bits trailing the given fromBit
*/
private static int precedingMaskFrom(int fromBit) {
int trailingZeros = Integer.numberOfTrailingZeros(fromBit);
if (trailingZeros == 0) return 0;
return -1 >>> -trailingZeros;
}
/**
* Get the object array used to call the function.
*
* @param callFlags the call flags
* @param context the context
* @param parameterOrder the order of parameters in the function
* @param receiverFunction the function that will create the receiver for this call, if applicable
* @return the call args
*/
public static Object[] getCallArgs(
int callFlags,
ExecutionContext context,
String[] parameterOrder,
CheckedSupplier<Object, CommandException> receiverFunction
) throws CommandException {
int leadingParameterNum = getLeadingCallArgNum(callFlags);
int cmdParameterNum = parameterOrder.length;
int trailingParameterNum = getTrailingCallArgNum(callFlags);
Object[] result = new Object[leadingParameterNum + cmdParameterNum + trailingParameterNum];
if (hasCallArg(callFlags, RECEIVER_BIT)) {
int index = getCallArgIndex(callFlags, RECEIVER_BIT, cmdParameterNum);
result[index] = receiverFunction.get();
}
if (hasCallArg(callFlags, SENDER_BIT)) {
int index = getCallArgIndex(callFlags, SENDER_BIT, cmdParameterNum);
result[index] = context.getSender();
}
if (hasCallArg(callFlags, CONTEXT_BIT)) {
int index = getCallArgIndex(callFlags, CONTEXT_BIT, cmdParameterNum);
result[index] = context;
}
if (hasCallArg(callFlags, CONTINUATION_BIT)) {
int index = getCallArgIndex(callFlags, CONTINUATION_BIT, cmdParameterNum);
result[index] = null; // filled in later.
}
for (int i = 0; i < parameterOrder.length; i++) {
String parameterName = parameterOrder[i];
result[leadingParameterNum + i] = context.get(parameterName);
}
return result;
}
}

View File

@@ -4,6 +4,8 @@ import io.dico.dicore.command.*;
import io.dico.dicore.command.annotation.Cmd; import io.dico.dicore.command.annotation.Cmd;
import io.dico.dicore.command.annotation.GenerateCommands; import io.dico.dicore.command.annotation.GenerateCommands;
import io.dico.dicore.command.parameter.type.IParameterTypeSelector; import io.dico.dicore.command.parameter.type.IParameterTypeSelector;
import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier;
import kotlin.coroutines.CoroutineContext;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -11,14 +13,11 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
public final class ReflectiveCommand extends Command { public final class ReflectiveCommand extends Command {
private static final int continuationMask = 1 << 3;
private final Cmd cmdAnnotation; private final Cmd cmdAnnotation;
private final Method method; private final Method method;
private final Object instance; private final Object instance;
private String[] parameterOrder; private String[] parameterOrder;
private final int callFlags;
// hasContinuation | hasContext | hasSender | hasReceiver
private final int flags;
ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException {
if (!method.isAnnotationPresent(Cmd.class)) { if (!method.isAnnotationPresent(Cmd.class)) {
@@ -48,7 +47,7 @@ public final class ReflectiveCommand extends Command {
this.method = method; this.method = method;
this.instance = instance; this.instance = instance;
this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters); this.callFlags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters);
} }
public Method getMethod() { public Method getMethod() {
@@ -59,12 +58,22 @@ public final class ReflectiveCommand extends Command {
return instance; return instance;
} }
public String getCmdName() { return cmdAnnotation.value(); } public String getCmdName() {
return cmdAnnotation.value();
}
public int getCallFlags() {
return callFlags;
}
void setParameterOrder(String[] parameterOrder) { void setParameterOrder(String[] parameterOrder) {
this.parameterOrder = parameterOrder; this.parameterOrder = parameterOrder;
} }
public int getParameterNum() {
return parameterOrder.length;
}
ICommandAddress getAddress() { ICommandAddress getAddress() {
ChildCommandAddress result = new ChildCommandAddress(); ChildCommandAddress result = new ChildCommandAddress();
result.setCommand(this); result.setCommand(this);
@@ -86,54 +95,24 @@ public final class ReflectiveCommand extends Command {
@Override @Override
public String execute(CommandSender sender, ExecutionContext context) throws CommandException { public String execute(CommandSender sender, ExecutionContext context) throws CommandException {
String[] parameterOrder = this.parameterOrder;
int extraArgumentCount = Integer.bitCount(flags);
int parameterStartIndex = Integer.bitCount(flags & ~continuationMask);
Object[] args = new Object[parameterOrder.length + extraArgumentCount]; CheckedSupplier<Object, CommandException> receiverFunction = () -> {
int i = 0;
int mask = 1;
if ((flags & mask) != 0) {
// Has receiver
try { try {
args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName()); return ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName());
} catch (Exception ex) { } catch (Exception ex) {
handleException(ex); handleException(ex);
return null; // unreachable return null; // unreachable
} }
};
Object[] callArgs = ReflectiveCallFlags.getCallArgs(callFlags, context, parameterOrder, receiverFunction);
if (ReflectiveCallFlags.hasCallArg(callFlags, ReflectiveCallFlags.CONTINUATION_BIT)) {
// If it has a continuation, call as coroutine
return callAsCoroutine(context, callArgs);
} }
mask <<= 1; return callSynchronously(callArgs);
if ((flags & mask) != 0) {
// Has sender
args[i++] = sender;
}
mask <<= 1;
if ((flags & mask) != 0) {
// Has context
args[i++] = context;
}
mask <<= 1;
if ((flags & mask) != 0) {
// Has continuation
extraArgumentCount--;
}
for (int n = args.length; i < n; i++) {
args[i] = context.get(parameterOrder[i - extraArgumentCount]);
}
if ((flags & mask) != 0) {
// Since it has continuation, call as coroutine
return callAsCoroutine(context, args);
}
return callSynchronously(args);
} }
private boolean isSuspendFunction() { private boolean isSuspendFunction() {
@@ -180,8 +159,11 @@ public final class ReflectiveCommand extends Command {
throw new CommandException("An internal error occurred while executing this command.", ex); throw new CommandException("An internal error occurred while executing this command.", ex);
} }
private String callAsCoroutine(ExecutionContext context, Object[] args) { private String callAsCoroutine(ExecutionContext executionContext, Object[] args) throws CommandException {
return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args); ICommandInterceptor factory = (ICommandInterceptor) instance;
CoroutineContext coroutineContext = (CoroutineContext) factory.getCoroutineContext(executionContext, method, getCmdName());
int continuationIndex = ReflectiveCallFlags.getCallArgIndex(callFlags, ReflectiveCallFlags.CONTINUATION_BIT, parameterOrder.length);
return KotlinReflectiveRegistrationKt.callCommandAsCoroutine(executionContext, coroutineContext, continuationIndex, method, instance, args);
} }
} }

View File

@@ -22,6 +22,8 @@ import java.util.function.Consumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
import static io.dico.dicore.command.registration.reflect.ReflectiveCallFlags.*;
/** /**
* Takes care of turning a reflection {@link Method} into a command and more. * Takes care of turning a reflection {@link Method} into a command and more.
*/ */
@@ -196,47 +198,43 @@ public class ReflectiveRegistration {
return new ReflectiveCommand(selector, method, instance).getAddress(); return new ReflectiveCommand(selector, method, instance).getAddress();
} }
static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException { static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] callParameters) throws CommandParseException {
ParameterList list = command.getParameterList(); ParameterList list = command.getParameterList();
boolean hasReceiverParameter = false;
boolean hasSenderParameter = false;
boolean hasContextParameter = false;
boolean hasContinuationParameter = false;
int start = 0;
int end = parameters.length;
Class<?> senderParameterType = null; Class<?> senderParameterType = null;
int flags = 0;
int start = 0;
int end = callParameters.length;
if (parameters.length > start if (callParameters.length > start
&& command.getInstance() instanceof ICommandInterceptor && command.getInstance() instanceof ICommandInterceptor
&& ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) { && ICommandReceiver.class.isAssignableFrom(callParameters[start].getType())) {
hasReceiverParameter = true; flags |= RECEIVER_BIT;
start++; ++start;
} }
if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) { if (callParameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = callParameters[start].getType())) {
hasSenderParameter = true; flags |= SENDER_BIT;
start++; ++start;
} }
if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) { if (callParameters.length > start && callParameters[start].getType() == ExecutionContext.class) {
hasContextParameter = true; flags |= CONTEXT_BIT;
start++; ++start;
} }
if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { if (callParameters.length > start && callParameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) {
hasContinuationParameter = true; flags |= CONTINUATION_BIT;
end--; --end;
} }
String[] parameterNames = lookupParameterNames(method, parameters, start); String[] parameterNames = lookupParameterNames(method, callParameters, start);
for (int i = start, n = end; i < n; i++) { for (int i = start, n = end; i < n; i++) {
Parameter<?, ?> parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]); Parameter<?, ?> parameter = parseParameter(selector, method, callParameters[i], parameterNames[i - start]);
list.addParameter(parameter); list.addParameter(parameter);
} }
command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
command.setParameterOrder(hasCallArg(flags, CONTINUATION_BIT) ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames);
RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class); RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class);
if (cmdPermissions != null) { if (cmdPermissions != null) {
@@ -277,6 +275,7 @@ public class ReflectiveRegistration {
command.setDescription(); command.setDescription();
} }
boolean hasSenderParameter = hasCallArg(flags, SENDER_BIT);
if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) { if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) {
command.addContextFilter(IContextFilter.PLAYER_ONLY); command.addContextFilter(IContextFilter.PLAYER_ONLY);
} else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) { } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) {
@@ -287,17 +286,9 @@ public class ReflectiveRegistration {
command.addContextFilter(IContextFilter.CONSOLE_ONLY); command.addContextFilter(IContextFilter.CONSOLE_ONLY);
} }
list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs()); list.setRepeatFinalParameter(callParameters.length > start && callParameters[callParameters.length - 1].isVarArgs());
list.setFinalParameterMayBeFlag(true); list.setFinalParameterMayBeFlag(true);
int flags = 0;
if (hasContinuationParameter) flags |= 1;
flags <<= 1;
if (hasContextParameter) flags |= 1;
flags <<= 1;
if (hasSenderParameter) flags |= 1;
flags <<= 1;
if (hasReceiverParameter) flags |= 1;
return flags; return flags;
} }

View File

@@ -17,20 +17,23 @@ fun isSuspendFunction(method: Method): Boolean {
return func.isSuspend return func.isSuspend
} }
fun callAsCoroutine( @Throws(CommandException::class)
command: ReflectiveCommand, fun callCommandAsCoroutine(
factory: ICommandInterceptor, executionContext: ExecutionContext,
context: ExecutionContext, coroutineContext: CoroutineContext,
continuationIndex: Int,
method: Method,
instance: Any?,
args: Array<Any?> args: Array<Any?>
): String? { ): String? {
val coroutineContext = factory.getCoroutineContext(context, command.method, command.cmdName) as CoroutineContext
// UNDISPATCHED causes the handler to run until the first suspension point on the current thread, // UNDISPATCHED causes the handler to run until the first suspension point on the current thread,
// meaning command handlers that don't have suspension points will run completely synchronously. // meaning command handlers that don't have suspension points will run completely synchronously.
// Tasks that take time to compute should suspend the coroutine and resume on another thread. // Tasks that take time to compute should suspend the coroutine and resume on another thread.
val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) { val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) {
suspendCoroutineUninterceptedOrReturn<Any?> { cont -> suspendCoroutineUninterceptedOrReturn<Any?> { cont ->
command.method.invoke(command.instance, *args, cont.intercepted()) args[continuationIndex] = cont.intercepted()
method.invoke(instance, *args)
} }
} }
@@ -39,12 +42,12 @@ fun callAsCoroutine(
} }
job.invokeOnCompletion { job.invokeOnCompletion {
val chatHandler = context.address.chatHandler val chatHandler = executionContext.address.chatHandler
try { try {
val result = job.getResult() val result = job.getResult()
chatHandler.sendMessage(context.sender, EMessageType.RESULT, result) chatHandler.sendMessage(executionContext.sender, EMessageType.RESULT, result)
} catch (ex: Throwable) { } catch (ex: Throwable) {
chatHandler.handleException(context.sender, context, ex) chatHandler.handleException(executionContext.sender, executionContext, ex)
} }
} }

View File

@@ -1,73 +1,73 @@
package io.dico.dicore.command.example; package io.dico.dicore.command.example;
import io.dico.dicore.command.CommandBuilder; import io.dico.dicore.command.CommandBuilder;
import io.dico.dicore.command.CommandException; import io.dico.dicore.command.CommandException;
import io.dico.dicore.command.Validate; import io.dico.dicore.command.Validate;
import io.dico.dicore.command.annotation.Cmd; import io.dico.dicore.command.annotation.Cmd;
import io.dico.dicore.command.parameter.ArgumentBuffer; import io.dico.dicore.command.parameter.ArgumentBuffer;
import io.dico.dicore.command.parameter.Parameter; import io.dico.dicore.command.parameter.Parameter;
import io.dico.dicore.command.parameter.type.ParameterConfig; import io.dico.dicore.command.parameter.type.ParameterConfig;
import io.dico.dicore.command.parameter.type.ParameterType; import io.dico.dicore.command.parameter.type.ParameterType;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
public class ParameterInfoObjectExample { public class ParameterInfoObjectExample {
private @interface ParentPermission { private @interface ParentPermission {
String value(); String value();
} }
static class MyInfoObject { static class MyInfoObject {
public static final ParameterConfig<ParentPermission, MyInfoObject> config = new ParameterConfig<ParentPermission, MyInfoObject>() { public static final ParameterConfig<ParentPermission, MyInfoObject> config = new ParameterConfig<ParentPermission, MyInfoObject>() {
@Override @Override
protected MyInfoObject toParameterInfo(ParentPermission annotation) { protected MyInfoObject toParameterInfo(ParentPermission annotation) {
return new MyInfoObject(annotation.value()); return new MyInfoObject(annotation.value());
} }
}; };
private String permissionParent; private String permissionParent;
MyInfoObject(String permissionParent) { MyInfoObject(String permissionParent) {
this.permissionParent = permissionParent; this.permissionParent = permissionParent;
} }
public String getPermissionParent() { public String getPermissionParent() {
return permissionParent; return permissionParent;
} }
} }
static class MyParameterType extends ParameterType<String, MyInfoObject> { static class MyParameterType extends ParameterType<String, MyInfoObject> {
public MyParameterType() { public MyParameterType() {
super(String.class, MyInfoObject.config); super(String.class, MyInfoObject.config);
} }
@Override @Override
public String parse(Parameter<String, MyInfoObject> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { public String parse(Parameter<String, MyInfoObject> parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException {
String value = buffer.next(); String value = buffer.next();
MyInfoObject mio = parameter.getParamInfo(); MyInfoObject mio = parameter.getParamInfo();
if (mio != null) { if (mio != null) {
String permission = mio.permissionParent + "." + value; String permission = mio.permissionParent + "." + value;
Validate.isAuthorized(sender, permission); Validate.isAuthorized(sender, permission);
} }
return value; return value;
} }
} }
static class MyCommands { static class MyCommands {
@Cmd("test") @Cmd("test")
Object cmdTest(@ParentPermission("test.permission") String value) { Object cmdTest(@ParentPermission("test.permission") String value) {
return "You have permission to use the argument '" + value + "'!"; return "You have permission to use the argument '" + value + "'!";
} }
} }
static void main(String[] args) { static void main(String[] args) {
new CommandBuilder() new CommandBuilder()
.addParameterType(false, new MyParameterType()) .addParameterType(false, new MyParameterType())
.registerCommands(new MyCommands()); .registerCommands(new MyCommands());
} }
} }

View File

@@ -1,182 +1,182 @@
package io.dico.dicore; package io.dico.dicore;
public interface BitModifier { public interface BitModifier {
long mask(); long mask();
long get(long x); long get(long x);
long set(long x, long value); long set(long x, long value);
default int lowerBound() { default int lowerBound() {
return Long.numberOfTrailingZeros(mask()); return Long.numberOfTrailingZeros(mask());
} }
default int upperBound() { default int upperBound() {
return 64 - Long.numberOfLeadingZeros(mask()); return 64 - Long.numberOfLeadingZeros(mask());
} }
default int bitCount() { default int bitCount() {
return Long.bitCount(mask()); return Long.bitCount(mask());
} }
default boolean getBoolean(long x) { default boolean getBoolean(long x) {
return get(x) == 1; return get(x) == 1;
} }
default long setBoolean(long x, boolean value) { default long setBoolean(long x, boolean value) {
return set(x, value ? 1 : 0); return set(x, value ? 1 : 0);
} }
default int getInt(long x) { default int getInt(long x) {
return (int) (get(x) & 0xFFFFFFFFL); return (int) (get(x) & 0xFFFFFFFFL);
} }
default long setInt(long x, int value) { default long setInt(long x, int value) {
return set(x, value & 0xFFFFFFFFL); return set(x, value & 0xFFFFFFFFL);
} }
default short getShort(long x) { default short getShort(long x) {
return (short) (get(x) & 0xFFFFL); return (short) (get(x) & 0xFFFFL);
} }
default long setShort(long x, int value) { default long setShort(long x, int value) {
return set(x, value & 0xFFFFL); return set(x, value & 0xFFFFL);
} }
default byte getByte(long x) { default byte getByte(long x) {
return (byte) (get(x) & 0xFFL); return (byte) (get(x) & 0xFFL);
} }
default long setByte(long x, int value) { default long setByte(long x, int value) {
return set(x, value & 0xFFL); return set(x, value & 0xFFL);
} }
final class OfSingle implements BitModifier { final class OfSingle implements BitModifier {
private final long mask; private final long mask;
public OfSingle(int bit) { public OfSingle(int bit) {
if (bit < 0 || bit >= 64) { if (bit < 0 || bit >= 64) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
this.mask = 1L << bit; this.mask = 1L << bit;
} }
@Override @Override
public int bitCount() { public int bitCount() {
return 1; return 1;
} }
@Override @Override
public long mask() { public long mask() {
return mask; return mask;
} }
public boolean getBoolean(long x) { public boolean getBoolean(long x) {
return (x & mask) != 0; return (x & mask) != 0;
} }
public long setBoolean(long x, boolean value) { public long setBoolean(long x, boolean value) {
return value ? (x | mask) : (x & ~mask); return value ? (x | mask) : (x & ~mask);
} }
@Override @Override
public long get(long x) { public long get(long x) {
return getBoolean(x) ? 1 : 0; return getBoolean(x) ? 1 : 0;
} }
@Override @Override
public long set(long x, long value) { public long set(long x, long value) {
if (value < 0 || value > 1) { if (value < 0 || value > 1) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return setBoolean(x, value == 1); return setBoolean(x, value == 1);
} }
} }
final class OfMultiple implements BitModifier { final class OfMultiple implements BitModifier {
private final int lowerBound; private final int lowerBound;
private final int bitCount; private final int bitCount;
private final long mask; private final long mask;
public OfMultiple(int lowerBound, int bitCount) { public OfMultiple(int lowerBound, int bitCount) {
int upperBound = lowerBound + bitCount; int upperBound = lowerBound + bitCount;
if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) { if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
this.lowerBound = lowerBound; this.lowerBound = lowerBound;
this.bitCount = bitCount; this.bitCount = bitCount;
this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound); this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound);
} }
@Override @Override
public int lowerBound() { public int lowerBound() {
return lowerBound; return lowerBound;
} }
@Override @Override
public int bitCount() { public int bitCount() {
return bitCount; return bitCount;
} }
@Override @Override
public int upperBound() { public int upperBound() {
return lowerBound + bitCount; return lowerBound + bitCount;
} }
@Override @Override
public long mask() { public long mask() {
return mask; return mask;
} }
@Override @Override
public long get(long x) { public long get(long x) {
return (x & mask) >>> lowerBound; return (x & mask) >>> lowerBound;
} }
@Override @Override
public long set(long x, long value) { public long set(long x, long value) {
return (x & ~mask) | ((value << lowerBound) & mask); return (x & ~mask) | ((value << lowerBound) & mask);
} }
} }
class Builder { class Builder {
int currentIndex; int currentIndex;
public int getCurrentIndex() { public int getCurrentIndex() {
return currentIndex; return currentIndex;
} }
public int getRemaining() { public int getRemaining() {
return 64 - currentIndex; return 64 - currentIndex;
} }
public OfSingle single() { public OfSingle single() {
checkAvailable(1); checkAvailable(1);
return new OfSingle(currentIndex++); return new OfSingle(currentIndex++);
} }
public BitModifier size(int size) { public BitModifier size(int size) {
if (size == 1) { if (size == 1) {
return single(); return single();
} }
checkAvailable(size); checkAvailable(size);
BitModifier result = new OfMultiple(currentIndex, size); BitModifier result = new OfMultiple(currentIndex, size);
currentIndex += size; currentIndex += size;
return result; return result;
} }
public BitModifier remaining() { public BitModifier remaining() {
return size(getRemaining()); return size(getRemaining());
} }
private void checkAvailable(int size) { private void checkAvailable(int size) {
if (size <= 0 || currentIndex + size > 64) { if (size <= 0 || currentIndex + size > 64) {
throw new IllegalStateException("Exceeding bit count of a long"); throw new IllegalStateException("Exceeding bit count of a long");
} }
} }
} }
} }

View File

@@ -1,295 +1,295 @@
/* /*
* Copyright (c) 2017 ProjectOreville * Copyright (c) 2017 ProjectOreville
* *
* All rights reserved. * All rights reserved.
* *
* Author(s): * Author(s):
* Dico Karssiens * Dico Karssiens
*/ */
package io.dico.dicore; package io.dico.dicore;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public final class Formatting implements CharSequence { public final class Formatting implements CharSequence {
public static final char FORMAT_CHAR = '\u00a7'; public static final char FORMAT_CHAR = '\u00a7';
private static final String CACHED_CHARS = "0123456789abcdefklmnor"; private static final String CACHED_CHARS = "0123456789abcdefklmnor";
private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()]; private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()];
@NotNull @NotNull
public static final Formatting public static final Formatting
BLACK = from('0'), BLACK = from('0'),
DARK_BLUE = from('1'), DARK_BLUE = from('1'),
DARL_GREEN = from('2'), DARL_GREEN = from('2'),
CYAN = from('3'), CYAN = from('3'),
DARK_RED = from('4'), DARK_RED = from('4'),
PURPLE = from('5'), PURPLE = from('5'),
ORANGE = from('6'), ORANGE = from('6'),
GRAY = from('7'), GRAY = from('7'),
DARK_GRAY = from('8'), DARK_GRAY = from('8'),
BLUE = from('9'), BLUE = from('9'),
GREEN = from('a'), GREEN = from('a'),
AQUA = from('b'), AQUA = from('b'),
RED = from('c'), RED = from('c'),
PINK = from('d'), PINK = from('d'),
YELLOW = from('e'), YELLOW = from('e'),
WHITE = from('f'), WHITE = from('f'),
BOLD = from('l'), BOLD = from('l'),
STRIKETHROUGH = from('m'), STRIKETHROUGH = from('m'),
UNDERLINE = from('n'), UNDERLINE = from('n'),
ITALIC = from('o'), ITALIC = from('o'),
MAGIC = from('k'), MAGIC = from('k'),
RESET = from('r'), RESET = from('r'),
EMPTY = from('\0'); EMPTY = from('\0');
public static String stripAll(String value) { public static String stripAll(String value) {
return stripAll(FORMAT_CHAR, value); return stripAll(FORMAT_CHAR, value);
} }
public static String stripAll(char alternateChar, String value) { public static String stripAll(char alternateChar, String value) {
int index = value.indexOf(alternateChar); int index = value.indexOf(alternateChar);
int max; int max;
if (index == -1 || index == (max = value.length() - 1)) { if (index == -1 || index == (max = value.length() - 1)) {
return value; return value;
} }
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
int from = 0; int from = 0;
do { do {
if (isRecognizedChar(value.charAt(index + 1))) { if (isRecognizedChar(value.charAt(index + 1))) {
result.append(value, from, index); result.append(value, from, index);
from = index + 2; from = index + 2;
} else { } else {
result.append(value, from, from = index + 2); result.append(value, from, from = index + 2);
} }
index = value.indexOf(alternateChar, index + 1); index = value.indexOf(alternateChar, index + 1);
} while (index != -1 && index != max && from <= max); } while (index != -1 && index != max && from <= max);
if (from <= max) { if (from <= max) {
result.append(value, from, value.length()); result.append(value, from, value.length());
} }
return result.toString(); return result.toString();
} }
public static String stripFirst(String value) { public static String stripFirst(String value) {
return stripFirst(FORMAT_CHAR, value); return stripFirst(FORMAT_CHAR, value);
} }
public static String stripFirst(char alternateChar, String value) { public static String stripFirst(char alternateChar, String value) {
int index = value.indexOf(alternateChar); int index = value.indexOf(alternateChar);
int max; int max;
if (index == -1 || index == (max = value.length() - 1)) { if (index == -1 || index == (max = value.length() - 1)) {
return value; return value;
} }
StringBuilder result = new StringBuilder(value.length()); StringBuilder result = new StringBuilder(value.length());
int from = 0; int from = 0;
if (isRecognizedChar(value.charAt(index + 1))) { if (isRecognizedChar(value.charAt(index + 1))) {
result.append(value, from, index); result.append(value, from, index);
from = index + 2; from = index + 2;
} else { } else {
result.append(value, from, from = index + 2); result.append(value, from, from = index + 2);
} }
if (from < max) { if (from < max) {
result.append(value, from, value.length()); result.append(value, from, value.length());
} }
return result.toString(); return result.toString();
} }
public static Formatting from(char c) { public static Formatting from(char c) {
if (isRecognizedChar(c)) { if (isRecognizedChar(c)) {
c = Character.toLowerCase(c); c = Character.toLowerCase(c);
int index = CACHED_CHARS.indexOf(c); int index = CACHED_CHARS.indexOf(c);
if (index == -1) return EMPTY; if (index == -1) return EMPTY;
Formatting res = singleCharInstances[index]; Formatting res = singleCharInstances[index];
if (res == null) { if (res == null) {
singleCharInstances[index] = res = new Formatting(c); singleCharInstances[index] = res = new Formatting(c);
} }
return res; return res;
} }
return EMPTY; return EMPTY;
} }
public static Formatting from(String chars) { public static Formatting from(String chars) {
return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0'); return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0');
} }
public static Formatting getFormats(String input) { public static Formatting getFormats(String input) {
return getFormats(input, FORMAT_CHAR); return getFormats(input, FORMAT_CHAR);
} }
public static Formatting getFormats(String input, char formatChar) { public static Formatting getFormats(String input, char formatChar) {
return getFormats(input, 0, input.length(), formatChar); return getFormats(input, 0, input.length(), formatChar);
} }
public static Formatting getFormats(String input, int start, int end, char formatChar) { public static Formatting getFormats(String input, int start, int end, char formatChar) {
if ((start < 0) || (start > end) || (end > input.length())) { if ((start < 0) || (start > end) || (end > input.length())) {
throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length()); throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length());
} }
boolean needsFormatChar = formatChar != '\0'; boolean needsFormatChar = formatChar != '\0';
char[] formats = new char[6]; char[] formats = new char[6];
// just make sure it's not the same as formatChar // just make sure it's not the same as formatChar
char previous = (char) (formatChar + 1); char previous = (char) (formatChar + 1);
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
char c = input.charAt(i); char c = input.charAt(i);
if (previous == formatChar || !needsFormatChar) { if (previous == formatChar || !needsFormatChar) {
if (isColourChar(c) || isResetChar(c)) { if (isColourChar(c) || isResetChar(c)) {
formats = new char[6]; formats = new char[6];
formats[0] = Character.toLowerCase(c); formats[0] = Character.toLowerCase(c);
} else if (isFormatChar(c)) { } else if (isFormatChar(c)) {
char format = Character.toLowerCase(c); char format = Character.toLowerCase(c);
for (int j = 0; j < 6; j++) { for (int j = 0; j < 6; j++) {
if (formats[j] == '\0') { if (formats[j] == '\0') {
formats[j] = format; formats[j] = format;
break; break;
} else if (formats[j] == format) { } else if (formats[j] == format) {
break; break;
} }
} }
} }
} }
previous = c; previous = c;
} }
return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats); return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats);
} }
public static String translate(String input) { public static String translate(String input) {
return translateChars('&', input); return translateChars('&', input);
} }
public static String translateChars(char alternateChar, String input) { public static String translateChars(char alternateChar, String input) {
return translateFormat(alternateChar, FORMAT_CHAR, input); return translateFormat(alternateChar, FORMAT_CHAR, input);
} }
public static String revert(String input) { public static String revert(String input) {
return revertChars('&', input); return revertChars('&', input);
} }
public static String revertChars(char alternateChar, String input) { public static String revertChars(char alternateChar, String input) {
return translateFormat(FORMAT_CHAR, alternateChar, input); return translateFormat(FORMAT_CHAR, alternateChar, input);
} }
public static String translateFormat(char fromChar, char toChar, String input) { public static String translateFormat(char fromChar, char toChar, String input) {
if (input == null) { if (input == null) {
return null; return null;
} }
int n = input.length(); int n = input.length();
if (n < 2) { if (n < 2) {
return input; return input;
} }
char[] result = null; char[] result = null;
char previous = input.charAt(0); char previous = input.charAt(0);
for (int i = 1; i < n; i++) { for (int i = 1; i < n; i++) {
char c = input.charAt(i); char c = input.charAt(i);
if (previous == fromChar && isRecognizedChar(c)) { if (previous == fromChar && isRecognizedChar(c)) {
if (result == null) { if (result == null) {
result = input.toCharArray(); result = input.toCharArray();
} }
result[i - 1] = toChar; result[i - 1] = toChar;
} }
previous = c; previous = c;
} }
return result == null ? input : String.valueOf(result); return result == null ? input : String.valueOf(result);
} }
public static void translate(StringBuilder input) { public static void translate(StringBuilder input) {
translateChars('&', input); translateChars('&', input);
} }
public static void translateChars(char alternateChar, StringBuilder input) { public static void translateChars(char alternateChar, StringBuilder input) {
translateFormat(alternateChar, FORMAT_CHAR, input); translateFormat(alternateChar, FORMAT_CHAR, input);
} }
public static void revert(StringBuilder input) { public static void revert(StringBuilder input) {
revertChars('&', input); revertChars('&', input);
} }
public static void revertChars(char alternateChar, StringBuilder input) { public static void revertChars(char alternateChar, StringBuilder input) {
translateFormat(FORMAT_CHAR, alternateChar, input); translateFormat(FORMAT_CHAR, alternateChar, input);
} }
public static void translateFormat(char fromChar, char toChar, StringBuilder input) { public static void translateFormat(char fromChar, char toChar, StringBuilder input) {
if (input == null) { if (input == null) {
return; return;
} }
int n = input.length(); int n = input.length();
if (n < 2) { if (n < 2) {
return; return;
} }
char previous = input.charAt(0); char previous = input.charAt(0);
for (int i = 1; i < n; i++) { for (int i = 1; i < n; i++) {
char c = input.charAt(i); char c = input.charAt(i);
if (previous == fromChar && isRecognizedChar(c)) { if (previous == fromChar && isRecognizedChar(c)) {
input.setCharAt(i -1, toChar); input.setCharAt(i -1, toChar);
} }
previous = c; previous = c;
} }
} }
private static boolean isRecognizedChar(char c) { private static boolean isRecognizedChar(char c) {
return isColourChar(c) || isFormatChar(c) || isResetChar(c); return isColourChar(c) || isFormatChar(c) || isResetChar(c);
} }
private static boolean isColourChar(char c) { private static boolean isColourChar(char c) {
return "0123456789abcdefABCDEF".indexOf(c) >= 0; return "0123456789abcdefABCDEF".indexOf(c) >= 0;
} }
private static boolean isResetChar(char c) { private static boolean isResetChar(char c) {
return c == 'r' || c == 'R'; return c == 'r' || c == 'R';
} }
private static boolean isFormatChar(char c) { private static boolean isFormatChar(char c) {
return "klmnoKLMNO".indexOf(c) >= 0; return "klmnoKLMNO".indexOf(c) >= 0;
} }
private final String format; private final String format;
private Formatting(char[] formats) { private Formatting(char[] formats) {
StringBuilder format = new StringBuilder(12); StringBuilder format = new StringBuilder(12);
for (char c : formats) { for (char c : formats) {
if (c != '\0') { if (c != '\0') {
format.append(FORMAT_CHAR).append(c); format.append(FORMAT_CHAR).append(c);
} else { } else {
break; break;
} }
} }
this.format = format.toString(); this.format = format.toString();
} }
private Formatting(char c) { private Formatting(char c) {
this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : ""; this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : "";
} }
@Override @Override
public int length() { public int length() {
return format.length(); return format.length();
} }
@Override @Override
public char charAt(int index) { public char charAt(int index) {
return format.charAt(index); return format.charAt(index);
} }
@Override @Override
public String subSequence(int start, int end) { public String subSequence(int start, int end) {
return format.substring(start, end); return format.substring(start, end);
} }
@Override @Override
public String toString() { public String toString() {
return format; return format;
} }
public String toString(char formatChar) { public String toString(char formatChar) {
return format.replace(FORMAT_CHAR, formatChar); return format.replace(FORMAT_CHAR, formatChar);
} }
} }

View File

@@ -1,173 +1,173 @@
package io.dico.dicore; package io.dico.dicore;
/** /**
* A chainable object * A chainable object
* <p> * <p>
* It is not possible to declare another upper bound for type parameter Subtype. * It is not possible to declare another upper bound for type parameter Subtype.
* However, it is required that it also extends the type parameter Element. * However, it is required that it also extends the type parameter Element.
* *
* @param <Subtype> the interface that is chainable * @param <Subtype> the interface that is chainable
* @param <Element> the element of the chain, this is a supertype of the subtype * @param <Element> the element of the chain, this is a supertype of the subtype
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> { public interface InterfaceChain<Element, Subtype extends InterfaceChain<Element, Subtype>> {
/** /**
* returns the empty InterfaceChain instance. * returns the empty InterfaceChain instance.
* *
* @return the empty InterfaceChain instance * @return the empty InterfaceChain instance
*/ */
Subtype getEmptyInstance(); Subtype getEmptyInstance();
/** /**
* returns a InterfaceChain with the last added element detached. * returns a InterfaceChain with the last added element detached.
* <p> * <p>
* if this InterfaceChain is the empty instance, the empty instance is returned. * if this InterfaceChain is the empty instance, the empty instance is returned.
* *
* @return a InterfaceChain with the last added element detached * @return a InterfaceChain with the last added element detached
* @implNote for the purpose of lambdas, the default implementation also returns the empty instance. * @implNote for the purpose of lambdas, the default implementation also returns the empty instance.
*/ */
default Subtype withoutLastNode() { default Subtype withoutLastNode() {
return getEmptyInstance(); return getEmptyInstance();
} }
/** /**
* returns the element that was inserted as the last element * returns the element that was inserted as the last element
* <p> * <p>
* For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)} * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)}
* would return the given element. * would return the given element.
* *
* @return the element that was inserted as the last element * @return the element that was inserted as the last element
* @implNote for the purpose of lambdas, the default implementation returns this object, * @implNote for the purpose of lambdas, the default implementation returns this object,
* which is required to implement the Element type parameter. * which is required to implement the Element type parameter.
*/ */
default Element getDelegateOfLastNode() { default Element getDelegateOfLastNode() {
//noinspection unchecked //noinspection unchecked
return (Element) this; return (Element) this;
} }
/** /**
* @return The number of elements chained from this InterfaceChain. * @return The number of elements chained from this InterfaceChain.
* @implNote for the purpose of lambdas, the default implementation returns 1. * @implNote for the purpose of lambdas, the default implementation returns 1.
*/ */
default int getElementCount() { default int getElementCount() {
return 1; return 1;
} }
/** /**
* Get a new InterfaceChain that includes the given Element. * Get a new InterfaceChain that includes the given Element.
* <p> * <p>
* The default implementation of the Subtype should look like this: * The default implementation of the Subtype should look like this:
* <pre> {@code * <pre> {@code
* if (element == null) { * if (element == null) {
* return this; * return this;
* } * }
* *
* int count = getElementCount() + 1; * int count = getElementCount() + 1;
* return new Subtype() { * return new Subtype() {
* \@Override * \@Override
* public void exampleElementMethod() { * public void exampleElementMethod() {
* try { * try {
* Subtype.this.exampleElementMethod(); * Subtype.this.exampleElementMethod();
* } finally { * } finally {
* element.exampleElementMethod(); * element.exampleElementMethod();
* } * }
* } * }
* *
* \@Override * \@Override
* public Subtype withoutLastNode() { * public Subtype withoutLastNode() {
* return Subtype.this; * return Subtype.this;
* } * }
* *
* \@Override * \@Override
* public Element getDelegateOfLastNode() { * public Element getDelegateOfLastNode() {
* return element; * return element;
* } * }
* *
* \@Override * \@Override
* public int getElementCount() { * public int getElementCount() {
* return count; * return count;
* } * }
* }; * };
* } * }
* </pre> * </pre>
* *
* @param element A new element to insert at the end of this InterfaceChain. * @param element A new element to insert at the end of this InterfaceChain.
* @return a new InterfaceChain that includes the given Element. * @return a new InterfaceChain that includes the given Element.
*/ */
Subtype withElement(Element element); Subtype withElement(Element element);
/** /**
* Append each of the elements to this InterfaceChain * Append each of the elements to this InterfaceChain
* *
* @param elements the elements to append * @param elements the elements to append
* @return a new InterfaceChain with the elements appended * @return a new InterfaceChain with the elements appended
*/ */
default Subtype withElements(Element... elements) { default Subtype withElements(Element... elements) {
Subtype result = (Subtype) this; Subtype result = (Subtype) this;
for (Element element : elements) { for (Element element : elements) {
result = result.withElement(element); result = result.withElement(element);
} }
return result; return result;
} }
/* /*
Example Subtypes implementation Example Subtypes implementation
public class Subtypes { public class Subtypes {
private Subtypes() { private Subtypes() {
} }
private static final Subtype empty = new Subtype() { private static final Subtype empty = new Subtype() {
@Override @Override
public void exampleElementMethod() { public void exampleElementMethod() {
} }
@Override @Override
public Subtype withElement(Element other) { public Subtype withElement(Element other) {
return Subtypes.singleton(other); return Subtypes.singleton(other);
} }
@Override @Override
public int getElementCount() { public int getElementCount() {
return 0; return 0;
} }
@Override @Override
public Element getDelegateOfLastNode() { public Element getDelegateOfLastNode() {
return null; return null;
} }
}; };
public static Subtype empty() { public static Subtype empty() {
return empty; return empty;
} }
public static Subtype singleton(Element element) { public static Subtype singleton(Element element) {
if (element instanceof Subtype) { if (element instanceof Subtype) {
return (Subtype) element; return (Subtype) element;
} }
if (element == null) { if (element == null) {
return empty(); return empty();
} }
return new Subtype() { return new Subtype() {
@Override @Override
public void exampleElementMethod() { public void exampleElementMethod() {
element.exampleElementMethod(); element.exampleElementMethod();
} }
@Override @Override
public Element getDelegateOfLastNode() { public Element getDelegateOfLastNode() {
return element; return element;
} }
}; };
} }
} }
*/ */
} }

View File

@@ -1,120 +1,120 @@
package io.dico.dicore; package io.dico.dicore;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class InventoryEventUtil { public class InventoryEventUtil {
private InventoryEventUtil() { private InventoryEventUtil() {
} }
public static ItemStack getNewItem(InventoryClickEvent event) { public static ItemStack getNewItem(InventoryClickEvent event) {
Inventory clicked = event.getInventory(); Inventory clicked = event.getInventory();
switch (event.getAction()) { switch (event.getAction()) {
case SWAP_WITH_CURSOR: case SWAP_WITH_CURSOR:
case PLACE_ALL: case PLACE_ALL:
return event.getCursor(); return event.getCursor();
case PICKUP_ALL: case PICKUP_ALL:
case HOTBAR_MOVE_AND_READD: case HOTBAR_MOVE_AND_READD:
case MOVE_TO_OTHER_INVENTORY: case MOVE_TO_OTHER_INVENTORY:
case DROP_ALL_SLOT: case DROP_ALL_SLOT:
case COLLECT_TO_CURSOR: case COLLECT_TO_CURSOR:
return null; return null;
case PICKUP_HALF: case PICKUP_HALF:
case PICKUP_SOME: case PICKUP_SOME:
ItemStack item = clicked.getItem(event.getSlot()).clone(); ItemStack item = clicked.getItem(event.getSlot()).clone();
item.setAmount(item.getAmount() / 2); item.setAmount(item.getAmount() / 2);
return item; return item;
case PICKUP_ONE: case PICKUP_ONE:
case DROP_ONE_SLOT: case DROP_ONE_SLOT:
item = clicked.getItem(event.getSlot()).clone(); item = clicked.getItem(event.getSlot()).clone();
item.setAmount(Math.max(0, item.getAmount() - 1)); item.setAmount(Math.max(0, item.getAmount() - 1));
return item; return item;
case PLACE_ONE: case PLACE_ONE:
item = event.getView().getCursor().clone(); item = event.getView().getCursor().clone();
item.setAmount(1); item.setAmount(1);
return item; return item;
case PLACE_SOME: case PLACE_SOME:
item = event.getView().getCursor().clone(); item = event.getView().getCursor().clone();
item.setAmount(item.getAmount() / 2); item.setAmount(item.getAmount() / 2);
return item; return item;
case HOTBAR_SWAP: case HOTBAR_SWAP:
return event.getView().getBottomInventory().getItem(event.getHotbarButton()); return event.getView().getBottomInventory().getItem(event.getHotbarButton());
default: default:
return clicked.getItem(event.getSlot()); return clicked.getItem(event.getSlot());
} }
} }
public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) { public static Map<Integer, ItemStack> deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) {
int addedAmount = added.getAmount(); int addedAmount = added.getAmount();
Map<Integer, ItemStack> rv = Collections.emptyMap(); Map<Integer, ItemStack> rv = Collections.emptyMap();
for (int n = inventory.getSize(), i = 0; i < n; i++) { for (int n = inventory.getSize(), i = 0; i < n; i++) {
if (addedAmount <= 0) break; if (addedAmount <= 0) break;
ItemStack current = inventory.getItem(i); ItemStack current = inventory.getItem(i);
if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) { if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) {
int count = current == null ? 0 : current.getAmount(); int count = current == null ? 0 : current.getAmount();
int max = (current == null ? added : current).getType().getMaxStackSize(); int max = (current == null ? added : current).getType().getMaxStackSize();
if (count < max) { if (count < max) {
int diff = max - count; int diff = max - count;
if (diff > addedAmount) { if (diff > addedAmount) {
diff = addedAmount; diff = addedAmount;
} }
addedAmount -= diff; addedAmount -= diff;
if (rv.isEmpty()) rv = new LinkedHashMap<>(); if (rv.isEmpty()) rv = new LinkedHashMap<>();
if (computeNewItem) { if (computeNewItem) {
current = (current == null ? added : current).clone(); current = (current == null ? added : current).clone();
current.setAmount(count + diff); current.setAmount(count + diff);
rv.put(i, current); rv.put(i, current);
} else { } else {
rv.put(i, null); rv.put(i, null);
} }
} }
} }
} }
return rv; return rv;
} }
public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) { public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) {
int heldItemSlot = inventory.getHeldItemSlot(); int heldItemSlot = inventory.getHeldItemSlot();
ItemStack heldItem = inventory.getItem(heldItemSlot); ItemStack heldItem = inventory.getItem(heldItemSlot);
if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) { if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) {
return null; return null;
} }
int amt = added.getAmount(); int amt = added.getAmount();
for (int i = 0; i < heldItemSlot; i++) { for (int i = 0; i < heldItemSlot; i++) {
ItemStack item = inventory.getItem(i); ItemStack item = inventory.getItem(i);
if (!SpigotUtil.isItemPresent(item)) { if (!SpigotUtil.isItemPresent(item)) {
return null; return null;
} }
if (item.isSimilar(item)) { if (item.isSimilar(item)) {
amt -= Math.max(0, item.getMaxStackSize() - item.getAmount()); amt -= Math.max(0, item.getMaxStackSize() - item.getAmount());
if (amt <= 0) { if (amt <= 0) {
return null; return null;
} }
} }
} }
added = added.clone(); added = added.clone();
added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount())); added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount()));
return added; return added;
} }
} }

View File

@@ -1,116 +1,116 @@
package io.dico.dicore; package io.dico.dicore;
import java.util.logging.Logger; import java.util.logging.Logger;
public interface Logging { public interface Logging {
void info(Object o); void info(Object o);
void warn(Object o); void warn(Object o);
void error(Object o); void error(Object o);
void debug(Object o); void debug(Object o);
void setDebugging(boolean debugging); void setDebugging(boolean debugging);
boolean isDebugging(); boolean isDebugging();
class RootLogging implements Logging { class RootLogging implements Logging {
private final String prefix; private final String prefix;
private final Logger root; private final Logger root;
private boolean debugging; private boolean debugging;
public RootLogging(String prefix, Logger root, boolean debugging) { public RootLogging(String prefix, Logger root, boolean debugging) {
this.root = root; this.root = root;
this.prefix = prefix; this.prefix = prefix;
this.debugging = debugging; this.debugging = debugging;
} }
@Override @Override
public void info(Object o) { public void info(Object o) {
root.info(prefix(o)); root.info(prefix(o));
} }
@Override @Override
public void warn(Object o) { public void warn(Object o) {
root.warning(prefix(o)); root.warning(prefix(o));
} }
@Override @Override
public void error(Object o) { public void error(Object o) {
root.severe(prefix(o)); root.severe(prefix(o));
} }
@Override @Override
public void debug(Object o) { public void debug(Object o) {
if (debugging) { if (debugging) {
root.info(String.format("[DEBUG] %s", prefix(o))); root.info(String.format("[DEBUG] %s", prefix(o)));
} }
} }
@Override @Override
public boolean isDebugging() { public boolean isDebugging() {
return debugging; return debugging;
} }
@Override @Override
public void setDebugging(boolean debugging) { public void setDebugging(boolean debugging) {
this.debugging = debugging; this.debugging = debugging;
} }
private String prefix(Object o) { private String prefix(Object o) {
return String.format("[%s] %s", prefix, String.valueOf(o)); return String.format("[%s] %s", prefix, String.valueOf(o));
} }
} }
class SubLogging implements Logging { class SubLogging implements Logging {
protected String prefix; protected String prefix;
private final Logging superLogger; private final Logging superLogger;
private boolean debugging; private boolean debugging;
public SubLogging(String prefix, Logging superLogger, boolean debugging) { public SubLogging(String prefix, Logging superLogger, boolean debugging) {
this.superLogger = superLogger; this.superLogger = superLogger;
this.prefix = prefix; this.prefix = prefix;
this.debugging = debugging; this.debugging = debugging;
} }
@Override @Override
public void info(Object o) { public void info(Object o) {
superLogger.info(prefix(o)); superLogger.info(prefix(o));
} }
@Override @Override
public void warn(Object o) { public void warn(Object o) {
superLogger.warn(prefix(o)); superLogger.warn(prefix(o));
} }
@Override @Override
public void error(Object o) { public void error(Object o) {
superLogger.error(prefix(o)); superLogger.error(prefix(o));
} }
@Override @Override
public void debug(Object o) { public void debug(Object o) {
if (debugging) { if (debugging) {
superLogger.info(String.format("[DEBUG] %s", prefix(o))); superLogger.info(String.format("[DEBUG] %s", prefix(o)));
} }
} }
@Override @Override
public boolean isDebugging() { public boolean isDebugging() {
return debugging; return debugging;
} }
@Override @Override
public void setDebugging(boolean debugging) { public void setDebugging(boolean debugging) {
this.debugging = debugging; this.debugging = debugging;
} }
private String prefix(Object o) { private String prefix(Object o) {
return String.format("[%s] %s", prefix, String.valueOf(o)); return String.format("[%s] %s", prefix, String.valueOf(o));
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,59 +1,59 @@
package io.dico.dicore; package io.dico.dicore;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class SetBasedWhitelist implements Whitelist { public class SetBasedWhitelist implements Whitelist {
private final Set set; private final Set set;
private final boolean blacklist; private final boolean blacklist;
public SetBasedWhitelist(Object[] array, boolean blacklist) { public SetBasedWhitelist(Object[] array, boolean blacklist) {
this(Arrays.asList(array), blacklist); this(Arrays.asList(array), blacklist);
} }
public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) { public SetBasedWhitelist(ConfigurationSection section, Function<String, ?> parser) {
this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()), this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()),
section.getBoolean("blacklist", false)); section.getBoolean("blacklist", false));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public SetBasedWhitelist(Collection collection, boolean blacklist) { public SetBasedWhitelist(Collection collection, boolean blacklist) {
Set set; Set set;
if (collection.isEmpty()) { if (collection.isEmpty()) {
set = Collections.emptySet(); set = Collections.emptySet();
} else if (collection.iterator().next() instanceof Enum) { } else if (collection.iterator().next() instanceof Enum) {
set = EnumSet.copyOf(collection); set = EnumSet.copyOf(collection);
} else if (collection instanceof Set) { } else if (collection instanceof Set) {
set = (Set) collection; set = (Set) collection;
} else { } else {
set = new HashSet<>(collection); set = new HashSet<>(collection);
} }
this.set = set; this.set = set;
this.blacklist = blacklist; this.blacklist = blacklist;
} }
@Override @Override
public boolean isWhitelisted(Object o) { public boolean isWhitelisted(Object o) {
return blacklist != set.contains(o); return blacklist != set.contains(o);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public String toString() { public String toString() {
return (blacklist ? "Blacklist" : "Whitelist") + "{" return (blacklist ? "Blacklist" : "Whitelist") + "{"
+ String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}"; + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}";
} }
public Set getSet() { public Set getSet() {
return set; return set;
} }
public boolean isBlacklist() { public boolean isBlacklist() {
return blacklist; return blacklist;
} }
} }

View File

@@ -1,408 +1,408 @@
package io.dico.dicore; package io.dico.dicore;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.*; import org.bukkit.entity.*;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable; import org.bukkit.material.Attachable;
import org.bukkit.material.MaterialData; import org.bukkit.material.MaterialData;
import org.bukkit.projectiles.ProjectileSource; import org.bukkit.projectiles.ProjectileSource;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
public class SpigotUtil { public class SpigotUtil {
private SpigotUtil() { private SpigotUtil() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public static World matchWorld(String input) { public static World matchWorld(String input) {
try { try {
UUID uid = UUID.fromString(input); UUID uid = UUID.fromString(input);
World world = Bukkit.getWorld(uid); World world = Bukkit.getWorld(uid);
if (world != null) { if (world != null) {
return world; return world;
} }
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
} }
World result = Bukkit.getWorld(input); World result = Bukkit.getWorld(input);
if (result == null) { if (result == null) {
input = input.toLowerCase().replace("_", "").replaceAll("[-_]", ""); input = input.toLowerCase().replace("_", "").replaceAll("[-_]", "");
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
if (world.getName().toLowerCase().equals(input)) { if (world.getName().toLowerCase().equals(input)) {
result = world; result = world;
break; break;
} }
} }
} }
return result; return result;
} }
public static Block getSupportingBlock(Block block) { public static Block getSupportingBlock(Block block) {
MaterialData data = block.getState().getData(); MaterialData data = block.getState().getData();
if (data instanceof Attachable) { if (data instanceof Attachable) {
BlockFace attachedOn = ((Attachable) data).getAttachedFace(); BlockFace attachedOn = ((Attachable) data).getAttachedFace();
return block.getRelative(attachedOn); return block.getRelative(attachedOn);
} }
return null; return null;
} }
public static boolean isItemPresent(ItemStack stack) { public static boolean isItemPresent(ItemStack stack) {
return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0; return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0;
} }
public static boolean removeItems(Inventory from, ItemStack item, int amount) { public static boolean removeItems(Inventory from, ItemStack item, int amount) {
for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) { for (Map.Entry<Integer, ? extends ItemStack> entry : from.all(item.getType()).entrySet()) {
ItemStack stack = entry.getValue(); ItemStack stack = entry.getValue();
if (item.isSimilar(stack)) { if (item.isSimilar(stack)) {
amount -= stack.getAmount(); amount -= stack.getAmount();
int stackAmount = -Math.min(0, amount); int stackAmount = -Math.min(0, amount);
if (stackAmount == 0) { if (stackAmount == 0) {
from.setItem(entry.getKey(), null); from.setItem(entry.getKey(), null);
} else { } else {
stack.setAmount(stackAmount); stack.setAmount(stackAmount);
} }
} }
} }
return amount <= 0; return amount <= 0;
} }
public static BlockFace yawToFace(float yaw) { public static BlockFace yawToFace(float yaw) {
if ((yaw %= 360) < 0) if ((yaw %= 360) < 0)
yaw += 360; yaw += 360;
if (45 <= yaw && yaw < 135) if (45 <= yaw && yaw < 135)
return BlockFace.WEST; return BlockFace.WEST;
if (135 <= yaw && yaw < 225) if (135 <= yaw && yaw < 225)
return BlockFace.NORTH; return BlockFace.NORTH;
if (225 <= yaw && yaw < 315) if (225 <= yaw && yaw < 315)
return BlockFace.EAST; return BlockFace.EAST;
return BlockFace.SOUTH; return BlockFace.SOUTH;
} }
public static void addItems(InventoryHolder entity, ItemStack... items) { public static void addItems(InventoryHolder entity, ItemStack... items) {
Location dropLocation; Location dropLocation;
if (entity instanceof Entity) { if (entity instanceof Entity) {
dropLocation = ((Entity) entity).getLocation(); dropLocation = ((Entity) entity).getLocation();
} else if (entity instanceof BlockState) { } else if (entity instanceof BlockState) {
dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5); dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5);
} else { } else {
throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity); throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity);
} }
World world = dropLocation.getWorld(); World world = dropLocation.getWorld();
for (ItemStack toDrop : entity.getInventory().addItem(items).values()) { for (ItemStack toDrop : entity.getInventory().addItem(items).values()) {
world.dropItemNaturally(dropLocation, toDrop); world.dropItemNaturally(dropLocation, toDrop);
} }
} }
public static String asJsonString(Object object) { public static String asJsonString(Object object) {
return asJsonString(null, object, 0); return asJsonString(null, object, 0);
} }
public static String asJsonString(String key, Object object, int indentation) { public static String asJsonString(String key, Object object, int indentation) {
String indent = new String(new char[indentation * 2]).replace('\0', ' '); String indent = new String(new char[indentation * 2]).replace('\0', ' ');
StringBuilder builder = new StringBuilder(indent); StringBuilder builder = new StringBuilder(indent);
if (key != null) { if (key != null) {
builder.append(key).append(": "); builder.append(key).append(": ");
} }
if (object instanceof ConfigurationSerializable) { if (object instanceof ConfigurationSerializable) {
object = ((ConfigurationSerializable) object).serialize(); object = ((ConfigurationSerializable) object).serialize();
} }
if (object instanceof Map) { if (object instanceof Map) {
builder.append("{\n"); builder.append("{\n");
Map<?, ?> map = (Map) object; Map<?, ?> map = (Map) object;
for (Map.Entry entry : map.entrySet()) { for (Map.Entry entry : map.entrySet()) {
builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1)); builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1));
} }
builder.append(indent).append("}"); builder.append(indent).append("}");
} else if (object instanceof List) { } else if (object instanceof List) {
builder.append("[\n"); builder.append("[\n");
List list = (List) object; List list = (List) object;
for (Object entry : list) { for (Object entry : list) {
builder.append(asJsonString(null, entry, indentation + 1)); builder.append(asJsonString(null, entry, indentation + 1));
} }
builder.append(indent).append("]"); builder.append(indent).append("]");
} else { } else {
builder.append(String.valueOf(object)); builder.append(String.valueOf(object));
} }
return builder.append(",\n").toString(); return builder.append(",\n").toString();
} }
public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) { public static String asJsonString(String key, Object object, int indentation, BiConsumer<List<?>, StringBuilder> listHeader) {
String indent = new String(new char[indentation * 2]).replace('\0', ' '); String indent = new String(new char[indentation * 2]).replace('\0', ' ');
StringBuilder builder = new StringBuilder(indent); StringBuilder builder = new StringBuilder(indent);
if (key != null) { if (key != null) {
builder.append(key).append(": "); builder.append(key).append(": ");
} }
if (object instanceof ConfigurationSerializable) { if (object instanceof ConfigurationSerializable) {
object = ((ConfigurationSerializable) object).serialize(); object = ((ConfigurationSerializable) object).serialize();
} }
if (object instanceof Map) { if (object instanceof Map) {
builder.append("{\n"); builder.append("{\n");
Map<?, ?> map = (Map) object; Map<?, ?> map = (Map) object;
for (Map.Entry entry : map.entrySet()) { for (Map.Entry entry : map.entrySet()) {
builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader)); builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader));
} }
builder.append(indent).append("}"); builder.append(indent).append("}");
} else if (object instanceof List) { } else if (object instanceof List) {
builder.append("["); builder.append("[");
List list = (List) object; List list = (List) object;
listHeader.accept(list, builder); listHeader.accept(list, builder);
builder.append("\n"); builder.append("\n");
for (Object entry : list) { for (Object entry : list) {
builder.append(asJsonString(null, entry, indentation + 1, listHeader)); builder.append(asJsonString(null, entry, indentation + 1, listHeader));
} }
builder.append(indent).append("]"); builder.append(indent).append("]");
} else { } else {
builder.append(String.valueOf(object)); builder.append(String.valueOf(object));
} }
return builder.append(",\n").toString(); return builder.append(",\n").toString();
} }
public static BlockFace estimateDirectionTo(Location from, Location to) { public static BlockFace estimateDirectionTo(Location from, Location to) {
double dx = from.getX() - to.getX(); double dx = from.getX() - to.getX();
double dz = from.getZ() - to.getZ(); double dz = from.getZ() - to.getZ();
boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0; boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0;
double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz); double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz);
dx *= f; dx *= f;
dz *= f; dz *= f;
double other = Math.abs(xGreater ? dz : dx); double other = Math.abs(xGreater ? dz : dx);
if (other <= .5) { if (other <= .5) {
return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH); return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH);
} }
if (other < 1.5) { if (other < 1.5) {
if (xGreater) { if (xGreater) {
return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST); return dx < 0 ? (dz < 0 ? BlockFace.WEST_NORTH_WEST : BlockFace.WEST_SOUTH_WEST) : (dz < 0 ? BlockFace.EAST_NORTH_EAST : BlockFace.EAST_SOUTH_EAST);
} }
return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST); return dx < 0 ? (dz < 0 ? BlockFace.NORTH_NORTH_WEST : BlockFace.SOUTH_SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_NORTH_EAST : BlockFace.SOUTH_SOUTH_EAST);
} }
return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST); return dx < 0 ? (dz < 0 ? BlockFace.NORTH_WEST : BlockFace.SOUTH_WEST) : (dz < 0 ? BlockFace.NORTH_EAST : BlockFace.SOUTH_EAST);
} }
public static Entity findEntityFromDamager(Entity damager, EntityType searched) { public static Entity findEntityFromDamager(Entity damager, EntityType searched) {
if (damager.getType() == searched) { if (damager.getType() == searched) {
return damager; return damager;
} }
if (damager instanceof Projectile) { if (damager instanceof Projectile) {
ProjectileSource shooter = ((Projectile) damager).getShooter(); ProjectileSource shooter = ((Projectile) damager).getShooter();
if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) { if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) {
return (Entity) shooter; return (Entity) shooter;
} }
return null; return null;
} }
if (damager.getType() == EntityType.PRIMED_TNT) { if (damager.getType() == EntityType.PRIMED_TNT) {
Entity source = ((TNTPrimed) damager).getSource(); Entity source = ((TNTPrimed) damager).getSource();
if (source.getType() == searched) { if (source.getType() == searched) {
return source; return source;
} }
} }
return null; return null;
} }
public static int xpForNextLevel(int currentLevel) { public static int xpForNextLevel(int currentLevel) {
if (currentLevel >= 30) { if (currentLevel >= 30) {
return 112 + (currentLevel - 30) * 9; return 112 + (currentLevel - 30) * 9;
} }
if (currentLevel >= 15) { if (currentLevel >= 15) {
return 37 + (currentLevel - 15) * 5; return 37 + (currentLevel - 15) * 5;
} }
return 7 + currentLevel * 2; return 7 + currentLevel * 2;
} }
public static int removeExp(Player entity, int xp) { public static int removeExp(Player entity, int xp) {
int total = entity.getTotalExperience(); int total = entity.getTotalExperience();
if (xp > total) { if (xp > total) {
xp = total; xp = total;
} }
int level = entity.getLevel(); int level = entity.getLevel();
if (level < 0) { if (level < 0) {
return 0; return 0;
} }
int removed = 0; int removed = 0;
int xpForNextLevel = xpForNextLevel(level); int xpForNextLevel = xpForNextLevel(level);
int current = (int) entity.getExp() * xpForNextLevel; int current = (int) entity.getExp() * xpForNextLevel;
if (xp > current) { if (xp > current) {
xp -= current; xp -= current;
total -= current; total -= current;
removed += current; removed += current;
if (level == 0) { if (level == 0) {
entity.setExp(0F); entity.setExp(0F);
entity.setTotalExperience(total); entity.setTotalExperience(total);
return removed; return removed;
} }
} else { } else {
current -= xp; current -= xp;
total -= xp; total -= xp;
removed += xp; removed += xp;
entity.setExp((float) current / xpForNextLevel); entity.setExp((float) current / xpForNextLevel);
entity.setTotalExperience(total); entity.setTotalExperience(total);
return removed; return removed;
} }
do { do {
xpForNextLevel = xpForNextLevel(--level); xpForNextLevel = xpForNextLevel(--level);
if (xpForNextLevel >= xp) { if (xpForNextLevel >= xp) {
total -= xp; total -= xp;
removed += xp; removed += xp;
entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp)); entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp));
entity.setTotalExperience(total); entity.setTotalExperience(total);
entity.setLevel(level); entity.setLevel(level);
return removed; return removed;
} }
xp -= xpForNextLevel; xp -= xpForNextLevel;
total -= xpForNextLevel; total -= xpForNextLevel;
removed += xpForNextLevel; removed += xpForNextLevel;
} while (level > 0); } while (level > 0);
entity.setExp(0F); entity.setExp(0F);
entity.setTotalExperience(0); entity.setTotalExperience(0);
entity.setLevel(0); entity.setLevel(0);
return removed; return removed;
} }
public static int getTotalExp(Player entity) { public static int getTotalExp(Player entity) {
int rv = 0; int rv = 0;
int level = Math.min(entity.getLevel(), 20000); int level = Math.min(entity.getLevel(), 20000);
for (int i = 0; i < level; i++) { for (int i = 0; i < level; i++) {
rv += xpForNextLevel(i); rv += xpForNextLevel(i);
} }
rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level); rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level);
return rv; return rv;
} }
public static double getTotalExpLevels(Player entity) { public static double getTotalExpLevels(Player entity) {
int xp = entity.getTotalExperience(); int xp = entity.getTotalExperience();
int level = 0; int level = 0;
while (xp > 0 && level < 20000) { while (xp > 0 && level < 20000) {
int needed = xpForNextLevel(level); int needed = xpForNextLevel(level);
if (needed > xp) { if (needed > xp) {
return level + ((double) xp / needed); return level + ((double) xp / needed);
} }
xp -= needed; xp -= needed;
level++; level++;
} }
return level; return level;
} }
public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) { public static int getNearbyPlayerCount(Player origin, double range, Predicate<Player> predicate) {
List<Entity> entities = origin.getNearbyEntities(range, range, range); List<Entity> entities = origin.getNearbyEntities(range, range, range);
int result = 0; int result = 0;
for (Entity entity : entities) { for (Entity entity : entities) {
if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
result++; result++;
} }
} }
return result; return result;
} }
public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) { public static void getNearbyPlayers(Player origin, double range, Collection<Player> collection, Predicate<Player> predicate) {
List<Entity> entities = origin.getNearbyEntities(range, range, range); List<Entity> entities = origin.getNearbyEntities(range, range, range);
for (Entity entity : entities) { for (Entity entity : entities) {
if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) {
collection.add((Player) entity); collection.add((Player) entity);
} }
} }
} }
public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) { public static void forEachNearbyPlayer(Player origin, double range, Consumer<Player> action) {
List<Entity> entities = origin.getNearbyEntities(range, range, range); List<Entity> entities = origin.getNearbyEntities(range, range, range);
for (Entity entity : entities) { for (Entity entity : entities) {
if (entity.getType() == EntityType.PLAYER) { if (entity.getType() == EntityType.PLAYER) {
action.accept((Player) entity); action.accept((Player) entity);
} }
} }
} }
public static double distanceSquared(Location first, Location second) { public static double distanceSquared(Location first, Location second) {
double dx = first.getX() - second.getX(); double dx = first.getX() - second.getX();
double dy = first.getY() - second.getY(); double dy = first.getY() - second.getY();
double dz = first.getZ() - second.getZ(); double dz = first.getZ() - second.getZ();
return dx * dx + dy * dy + dz * dz; return dx * dx + dy * dy + dz * dz;
} }
public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) { public static <T extends Entity> Iterator<T> findNearbyEntities(Entity origin, boolean includeSelf, Predicate<Entity> predicate, double horizontalRange, double verticalRange) {
Objects.requireNonNull(origin); Objects.requireNonNull(origin);
return new Iterator<T>() { return new Iterator<T>() {
Entity next; Entity next;
List<Entity> nearby; List<Entity> nearby;
int index = 0; int index = 0;
int size; int size;
{ {
if (includeSelf) { if (includeSelf) {
next = origin; next = origin;
} else { } else {
next = findNext(); next = findNext();
} }
} }
Entity findNext() { Entity findNext() {
if (nearby == null) { if (nearby == null) {
nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange); nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange);
size = nearby.size(); size = nearby.size();
} }
while (index < size) { while (index < size) {
Entity e = nearby.get(index++); Entity e = nearby.get(index++);
if (predicate.test(e)) { if (predicate.test(e)) {
return e; return e;
} }
} }
return null; return null;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return next != null; return next != null;
} }
@Override @Override
public T next() { public T next() {
if (next == null) { if (next == null) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
Entity result = next; Entity result = next;
next = findNext(); next = findNext();
//noinspection unchecked //noinspection unchecked
return (T) result; return (T) result;
} }
}; };
} }
} }

View File

@@ -1,473 +1,473 @@
package io.dico.dicore; package io.dico.dicore;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream; import java.util.stream.IntStream;
public class StringUtil { public class StringUtil {
public static String capitalize(String input) { public static String capitalize(String input) {
if (input.length() > 0) { if (input.length() > 0) {
char first = input.charAt(0); char first = input.charAt(0);
if (first != (first = Character.toUpperCase(first))) { if (first != (first = Character.toUpperCase(first))) {
char[] result = input.toCharArray(); char[] result = input.toCharArray();
result[0] = first; result[0] = first;
return String.valueOf(result); return String.valueOf(result);
} }
} }
return input; return input;
} }
/** /**
* Capitalizes the first character of the string or the first character of each word * Capitalizes the first character of the string or the first character of each word
* *
* @param input the string to capitalize * @param input the string to capitalize
* @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of
* the input is capitalized. * the input is capitalized.
* @return the capitalized string * @return the capitalized string
*/ */
public static String capitalize(String input, char spaceChar) { public static String capitalize(String input, char spaceChar) {
if (spaceChar == '\0') { if (spaceChar == '\0') {
return capitalize(input); return capitalize(input);
} }
char[] result = null; char[] result = null;
boolean capitalize = true; boolean capitalize = true;
for (int n = input.length(), i = 0; i < n; i++) { for (int n = input.length(), i = 0; i < n; i++) {
char c = input.charAt(i); char c = input.charAt(i);
if (capitalize && c != (c = Character.toUpperCase(c))) { if (capitalize && c != (c = Character.toUpperCase(c))) {
if (result == null) result = input.toCharArray(); if (result == null) result = input.toCharArray();
result[i] = c; result[i] = c;
} }
capitalize = c == spaceChar; capitalize = c == spaceChar;
} }
return result != null ? String.valueOf(result) : input; return result != null ? String.valueOf(result) : input;
} }
public static String capitalize(String input, char spaceChar, char newSpaceChar) { public static String capitalize(String input, char spaceChar, char newSpaceChar) {
if (newSpaceChar == '\0') { if (newSpaceChar == '\0') {
return capitalize(input, spaceChar); return capitalize(input, spaceChar);
} }
char[] result = null; char[] result = null;
boolean capitalize = true; boolean capitalize = true;
for (int n = input.length(), i = 0; i < n; i++) { for (int n = input.length(), i = 0; i < n; i++) {
char c = input.charAt(i); char c = input.charAt(i);
if (capitalize && c != (c = Character.toUpperCase(c))) { if (capitalize && c != (c = Character.toUpperCase(c))) {
if (result == null) result = input.toCharArray(); if (result == null) result = input.toCharArray();
result[i] = c; result[i] = c;
} }
if (capitalize = c == spaceChar) { if (capitalize = c == spaceChar) {
if (result == null) result = input.toCharArray(); if (result == null) result = input.toCharArray();
result[i] = newSpaceChar; result[i] = newSpaceChar;
} }
} }
return result != null ? String.valueOf(result) : input; return result != null ? String.valueOf(result) : input;
} }
/** /**
* Returns a lowercase version of the input with _ replaced with a space. * Returns a lowercase version of the input with _ replaced with a space.
* Mainly used for making names of enum constants readable. * Mainly used for making names of enum constants readable.
* *
* @param input * @param input
* @return a humanified version of @code input * @return a humanified version of @code input
*/ */
public static String humanify(String input) { public static String humanify(String input) {
return input == null ? null : input.toLowerCase().replace('_', ' '); return input == null ? null : input.toLowerCase().replace('_', ' ');
} }
/** /**
* Enumerate the given items, separating them by ", " and finally by " and " * Enumerate the given items, separating them by ", " and finally by " and "
* *
* @param words the items to enumerate (it's not really enumerating....) * @param words the items to enumerate (it's not really enumerating....)
* @return the enumerated string * @return the enumerated string
*/ */
public static String enumerate(String... words) { public static String enumerate(String... words) {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
int size = words.length; int size = words.length;
int secondLastIndex = size - 2; int secondLastIndex = size - 2;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
String word = words[i]; String word = words[i];
if (word.isEmpty()) if (word.isEmpty())
continue; continue;
result.append(word); result.append(word);
if (i < secondLastIndex) if (i < secondLastIndex)
result.append(", "); result.append(", ");
else if (i == secondLastIndex) else if (i == secondLastIndex)
result.append(" and "); result.append(" and ");
} }
return result.toString(); return result.toString();
} }
public static String enumerate(String list, String regex) { public static String enumerate(String list, String regex) {
return enumerate(list.split(regex)); return enumerate(list.split(regex));
} }
/** /**
* Return a formatted string of the length in millis, containing days, hours and minutes. * Return a formatted string of the length in millis, containing days, hours and minutes.
* *
* @param length The delay in milliseconds * @param length The delay in milliseconds
* @return the formatted string * @return the formatted string
*/ */
public static String getTimeLength(long length) { public static String getTimeLength(long length) {
int minute = 60000; // in millis int minute = 60000; // in millis
int hour = 60 * minute; int hour = 60 * minute;
int day = 24 * hour; int day = 24 * hour;
int minutes = (int) ((length / minute) % 60); int minutes = (int) ((length / minute) % 60);
int hours = (int) ((length / hour) % 24); int hours = (int) ((length / hour) % 24);
int days = (int) (length / day); //returns floor int days = (int) (length / day); //returns floor
String result = ""; // It will be splitted at "|" String result = ""; // It will be splitted at "|"
if (days != 0) if (days != 0)
result += days + " days|"; result += days + " days|";
if (hours != 0) if (hours != 0)
result += hours + " hours|"; result += hours + " hours|";
if (minutes != 0) if (minutes != 0)
result += minutes + " minutes|"; result += minutes + " minutes|";
return enumerate(result.split("\\|")); return enumerate(result.split("\\|"));
} }
/** /**
* Return a formatted String to represent the given time length, in the given units * Return a formatted String to represent the given time length, in the given units
* *
* @param sourceAmount Amount of delay * @param sourceAmount Amount of delay
* @param sourceUnit Unit of delay * @param sourceUnit Unit of delay
* @param ifEmpty the String to return if the * @param ifEmpty the String to return if the
* @param displayedUnits units displayed * @param displayedUnits units displayed
* @return the formatted string * @return the formatted string
* @throws IllegalArgumentException if there are no displayed units * @throws IllegalArgumentException if there are no displayed units
*/ */
public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) { public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) {
if (displayedUnits.length == 0) { if (displayedUnits.length == 0) {
throw new IllegalArgumentException("No displayed units"); throw new IllegalArgumentException("No displayed units");
} }
Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest) Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest)
List<String> segments = new ArrayList<>(displayedUnits.length); List<String> segments = new ArrayList<>(displayedUnits.length);
for (TimeUnit unit : displayedUnits) { for (TimeUnit unit : displayedUnits) {
long displayedAmount = unit.convert(sourceAmount, sourceUnit); long displayedAmount = unit.convert(sourceAmount, sourceUnit);
sourceAmount -= sourceUnit.convert(displayedAmount, unit); sourceAmount -= sourceUnit.convert(displayedAmount, unit);
if (displayedAmount > 0) { if (displayedAmount > 0) {
String unitWord = unit.name().toLowerCase(); // plural String unitWord = unit.name().toLowerCase(); // plural
if (displayedAmount == 1) { if (displayedAmount == 1) {
unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end
} }
segments.add(displayedAmount + " " + unitWord); segments.add(displayedAmount + " " + unitWord);
} }
} }
return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()])); return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()]));
} }
/** /**
* Returns the delay represented by a ban-like delay representation, in milliseconds * Returns the delay represented by a ban-like delay representation, in milliseconds
* Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds. * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds.
* <p> * <p>
* Supported characters are s, m, h, d, w. * Supported characters are s, m, h, d, w.
* Negative numbers are supported. * Negative numbers are supported.
* *
* @param input The input string * @param input The input string
* @return The delay in milliseconds * @return The delay in milliseconds
* @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized). * @throws IllegalArgumentException if the input string isn't properly formatted, or any non-digit character isn't recognized (capitals are not recognized).
*/ */
public static long getTimeLength(String input) { //if -1: error public static long getTimeLength(String input) { //if -1: error
long count = 0; long count = 0;
int i = 0; int i = 0;
while (i < input.length()) { while (i < input.length()) {
int num = 0; int num = 0;
char unit = '\0'; char unit = '\0';
boolean negate; boolean negate;
if (negate = input.charAt(i) == '-') { if (negate = input.charAt(i) == '-') {
i++; i++;
} }
do { do {
char c = input.charAt(i); char c = input.charAt(i);
int digit = c - '0'; int digit = c - '0';
if (0 <= digit && digit < 10) { if (0 <= digit && digit < 10) {
num = 10 * num + digit; num = 10 * num + digit;
} else { } else {
unit = c; unit = c;
break; break;
} }
} while (i < input.length()); } while (i < input.length());
long unitTime = getUnitTime(unit); long unitTime = getUnitTime(unit);
if (unitTime == -1) if (unitTime == -1)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (negate) { if (negate) {
unitTime = -unitTime; unitTime = -unitTime;
} }
count += (num * unitTime); count += (num * unitTime);
} }
return count; return count;
} }
/** /**
* Returns the time represented by the given unit character in milliseconds. * Returns the time represented by the given unit character in milliseconds.
* <p> * <p>
* 's' -> 1000 * 's' -> 1000
* 'm' -> 1000 * 60 * 'm' -> 1000 * 60
* 'h' -> 1000 * 60 * 60 * 'h' -> 1000 * 60 * 60
* 'd' -> 1000 * 60 * 60 * 24 * 'd' -> 1000 * 60 * 60 * 24
* 'w' -> 1000 * 60 * 60 * 24 * 7 * 'w' -> 1000 * 60 * 60 * 24 * 7
* anything else -> -1 * anything else -> -1
* *
* @param unit The unit character, as shown above * @param unit The unit character, as shown above
* @return the millisecond delay represented by the unit * @return the millisecond delay represented by the unit
*/ */
public static long getUnitTime(char unit) { //if -1: no value found public static long getUnitTime(char unit) { //if -1: no value found
switch (Character.toLowerCase(unit)) { switch (Character.toLowerCase(unit)) {
case 's': case 's':
return 1000; return 1000;
case 'm': case 'm':
return 1000 * 60; return 1000 * 60;
case 'h': case 'h':
return 1000 * 60 * 60; return 1000 * 60 * 60;
case 'd': case 'd':
return 1000 * 60 * 60 * 24; return 1000 * 60 * 60 * 24;
case 'w': case 'w':
return 1000 * 60 * 60 * 24 * 7; return 1000 * 60 * 60 * 24 * 7;
default: default:
return -1; return -1;
} }
} }
/** /**
* Computes a binary representation of the value. * Computes a binary representation of the value.
* The returned representation always displays 64 bits. * The returned representation always displays 64 bits.
* Every 8 bits, the digits are seperated by an _ * Every 8 bits, the digits are seperated by an _
* The representation is prefixed by 0b. * The representation is prefixed by 0b.
* <p> * <p>
* Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010 * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010
* *
* @param entry the value to represent in binary * @param entry the value to represent in binary
* @return A binary representation of the long value * @return A binary representation of the long value
*/ */
public static String toBinaryString(long entry) { public static String toBinaryString(long entry) {
String binary = Long.toBinaryString(entry); String binary = Long.toBinaryString(entry);
String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary; String binary64 = String.valueOf(new char[64 - binary.length()]).replace('\0', '0') + binary;
String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new)); String withUnderscores = String.join("_", IntStream.range(0, 8).mapToObj(x -> binary64.substring(x * 8, x * 8 + 8)).toArray(String[]::new));
return "0b" + withUnderscores; return "0b" + withUnderscores;
} }
/** /**
* Turns a generic java classname into a name formatted properly to be an enum constant. * Turns a generic java classname into a name formatted properly to be an enum constant.
* *
* @param name The string value I'd describe as a generic java classname (so we have CapitalCase) * @param name The string value I'd describe as a generic java classname (so we have CapitalCase)
* @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE) * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE)
*/ */
public static String toEnumFormat(String name) { public static String toEnumFormat(String name) {
StringBuilder result = new StringBuilder(name.length() + 2); StringBuilder result = new StringBuilder(name.length() + 2);
boolean capital = true; boolean capital = true;
for (int i = 0, n = name.length(); i < n; i++) { for (int i = 0, n = name.length(); i < n; i++) {
char c = name.charAt(i); char c = name.charAt(i);
if (capital) { if (capital) {
capital = Character.isUpperCase(c); capital = Character.isUpperCase(c);
} else if (Character.isUpperCase(c)) { } else if (Character.isUpperCase(c)) {
capital = true; capital = true;
result.append('_'); result.append('_');
} }
result.append(capital ? c : Character.toUpperCase(c)); result.append(capital ? c : Character.toUpperCase(c));
} }
return result.toString(); return result.toString();
} }
/** /**
* Replaces any occurrence of toReplace with another string. * Replaces any occurrence of toReplace with another string.
* Any colours that occured before the occurence of toReplace, are copied to the end of the replacement. * Any colours that occured before the occurence of toReplace, are copied to the end of the replacement.
* *
* @param target The String to query * @param target The String to query
* @param toReplace The sequence to replace * @param toReplace The sequence to replace
* @param with the replacing sequence * @param with the replacing sequence
* @return the result * @return the result
*/ */
public static String replaceKeepColours(String target, String toReplace, String with) { public static String replaceKeepColours(String target, String toReplace, String with) {
int index = -toReplace.length(); int index = -toReplace.length();
while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) { while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) {
String start = target.substring(0, index); String start = target.substring(0, index);
Formatting coloursBefore = Formatting.getFormats(start); Formatting coloursBefore = Formatting.getFormats(start);
String after; String after;
try { try {
after = target.substring(index + toReplace.length()); after = target.substring(index + toReplace.length());
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
after = ""; after = "";
} }
target = start + with + coloursBefore + after; target = start + with + coloursBefore + after;
} }
return target; return target;
} }
public static String replParam(String target, String param, Object repl) { public static String replParam(String target, String param, Object repl) {
return replParam(target, param, repl, false); return replParam(target, param, repl, false);
} }
public static String replParams(String target, String[] params, Object[] repls) { public static String replParams(String target, String[] params, Object[] repls) {
return replParams(target, params, repls, false, false); return replParams(target, params, repls, false, false);
} }
public static boolean replParams(String[] target, String[] params, Object[] repls) { public static boolean replParams(String[] target, String[] params, Object[] repls) {
return replParams(target, 0, target.length, params, repls); return replParams(target, 0, target.length, params, repls);
} }
public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) { public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) {
return replParams(target, from, to, params, repls, false); return replParams(target, from, to, params, repls, false);
} }
public static boolean replParams(List<String> target, String[] params, Object[] repls) { public static boolean replParams(List<String> target, String[] params, Object[] repls) {
return replParams(target, 0, target.size(), params, repls); return replParams(target, 0, target.size(), params, repls);
} }
public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) { public static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls) {
return replParams(target, from, to, params, repls, false); return replParams(target, from, to, params, repls, false);
} }
public static String replParamAndTranslate(String target, String param, Object repl) { public static String replParamAndTranslate(String target, String param, Object repl) {
return replParam(target, param, repl, true); return replParam(target, param, repl, true);
} }
public static String replParamsAndTranslate(String target, String[] params, Object[] repls) { public static String replParamsAndTranslate(String target, String[] params, Object[] repls) {
return replParams(target, params, repls, false, true); return replParams(target, params, repls, false, true);
} }
public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) { public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) {
return replParamsAndTranslate(target, 0, target.length, params, repls); return replParamsAndTranslate(target, 0, target.length, params, repls);
} }
public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) { public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) {
return replParams(target, from, to, params, repls, true); return replParams(target, from, to, params, repls, true);
} }
public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) { public static boolean replParamsAndTranslate(List<String> target, String[] params, Object[] repls) {
return replParamsAndTranslate(target, 0, target.size(), params, repls); return replParamsAndTranslate(target, 0, target.size(), params, repls);
} }
public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) { public static boolean replParamsAndTranslate(List<String> target, int from, int to, String[] params, Object[] repls) {
return replParams(target, from, to, params, repls, true); return replParams(target, from, to, params, repls, true);
} }
private static String replParam(String target, String param, Object replacementObj, boolean translate) { private static String replParam(String target, String param, Object replacementObj, boolean translate) {
int idx = target.indexOf(param, 0); int idx = target.indexOf(param, 0);
if (idx == -1) { if (idx == -1) {
return translate ? Formatting.translate(target) : target; return translate ? Formatting.translate(target) : target;
} }
String rep = replacementObj.toString(); String rep = replacementObj.toString();
StringBuilder builder = new StringBuilder(target); StringBuilder builder = new StringBuilder(target);
do { do {
builder.replace(idx, idx + param.length(), rep); builder.replace(idx, idx + param.length(), rep);
idx = builder.indexOf(param, idx + rep.length()); idx = builder.indexOf(param, idx + rep.length());
} while (idx != -1); } while (idx != -1);
if (translate) { if (translate) {
Formatting.translate(builder); Formatting.translate(builder);
} }
return builder.toString(); return builder.toString();
} }
@SuppressWarnings("StringEquality") @SuppressWarnings("StringEquality")
private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) { private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) {
if (from < 0 || to < from || to > target.length) { if (from < 0 || to < from || to > target.length) {
throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to); throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to);
} }
boolean change = false; boolean change = false;
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
String val = target[i]; String val = target[i];
if (val != (val = replParams(val, params, repls, true, translate))) { if (val != (val = replParams(val, params, repls, true, translate))) {
target[i] = val; target[i] = val;
change = true; change = true;
} }
} }
return change; return change;
} }
@SuppressWarnings("StringEquality") @SuppressWarnings("StringEquality")
private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) { private static boolean replParams(List<String> target, int from, int to, String[] params, Object[] repls, boolean translate) {
if (from < 0 || to < from || to > target.size()) { if (from < 0 || to < from || to > target.size()) {
throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to); throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to);
} }
boolean change = false; boolean change = false;
if (target instanceof RandomAccess) { if (target instanceof RandomAccess) {
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
String val = target.get(i); String val = target.get(i);
if (val != (val = replParams(val, params, repls, true, translate))) { if (val != (val = replParams(val, params, repls, true, translate))) {
target.set(i, val); target.set(i, val);
change = true; change = true;
} }
} }
} else { } else {
ListIterator<String> itr = target.listIterator(from); ListIterator<String> itr = target.listIterator(from);
for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) { for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) {
String val = itr.next(); String val = itr.next();
if (val != (val = replParams(val, params, repls, true, translate))) { if (val != (val = replParams(val, params, repls, true, translate))) {
itr.set(val); itr.set(val);
change = true; change = true;
} }
} }
} }
return change; return change;
} }
private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) { private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) {
int n = params.length; int n = params.length;
if (n != repls.length) { if (n != repls.length) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
String param = null; String param = null;
int idx = -1; int idx = -1;
int i; int i;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
param = params[i]; param = params[i];
if (param == null) { if (param == null) {
continue; continue;
} }
idx = target.indexOf(param, 0); idx = target.indexOf(param, 0);
if (idx != -1) { if (idx != -1) {
break; break;
} }
} }
if (idx == -1) { if (idx == -1) {
return translate ? Formatting.translate(target) : target; return translate ? Formatting.translate(target) : target;
} }
String repl = repls[i].toString(); String repl = repls[i].toString();
if (updateRepls) { if (updateRepls) {
repls[i] = repl; repls[i] = repl;
} }
StringBuilder builder = new StringBuilder(target); StringBuilder builder = new StringBuilder(target);
do { do {
builder.replace(idx, idx + param.length(), repl); builder.replace(idx, idx + param.length(), repl);
idx = builder.indexOf(param, idx + repl.length()); idx = builder.indexOf(param, idx + repl.length());
} while (idx != -1); } while (idx != -1);
for (i++; i < n; i++) { for (i++; i < n; i++) {
param = params[i]; param = params[i];
if (param == null || (idx = builder.indexOf(param, 0)) == -1) { if (param == null || (idx = builder.indexOf(param, 0)) == -1) {
continue; continue;
} }
repl = repls[i].toString(); repl = repls[i].toString();
if (updateRepls) { if (updateRepls) {
repls[i] = repl; repls[i] = repl;
} }
do { do {
builder.replace(idx, idx + param.length(), repl); builder.replace(idx, idx + param.length(), repl);
idx = builder.indexOf(param, idx + repl.length()); idx = builder.indexOf(param, idx + repl.length());
} while (idx != -1); } while (idx != -1);
} }
if (translate) { if (translate) {
Formatting.translate(builder); Formatting.translate(builder);
} }
return builder.toString(); return builder.toString();
} }
} }

View File

@@ -1,91 +1,91 @@
package io.dico.dicore; package io.dico.dicore;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@FunctionalInterface @FunctionalInterface
public interface Whitelist extends Predicate { public interface Whitelist extends Predicate {
Whitelist EVERYTHING = item -> true; Whitelist EVERYTHING = item -> true;
Whitelist NOTHING = item -> false; Whitelist NOTHING = item -> false;
Whitelist NOT_NULL = Objects::nonNull; Whitelist NOT_NULL = Objects::nonNull;
static <T> Predicate<T> everythingAsPredicate() { static <T> Predicate<T> everythingAsPredicate() {
return (Predicate<T>) EVERYTHING; return (Predicate<T>) EVERYTHING;
} }
static <T> Predicate<T> nothingAsPredicate() { static <T> Predicate<T> nothingAsPredicate() {
return (Predicate<T>) NOTHING; return (Predicate<T>) NOTHING;
} }
static <T> Predicate<T> notNullAsPredicate() { static <T> Predicate<T> notNullAsPredicate() {
return (Predicate<T>) NOT_NULL; return (Predicate<T>) NOT_NULL;
} }
static Whitelist only(Object item) { static Whitelist only(Object item) {
return item::equals; return item::equals;
} }
static Whitelist not(Object item) { static Whitelist not(Object item) {
return o -> !item.equals(o); return o -> !item.equals(o);
} }
static Whitelist only(Object item1, Object item2) { static Whitelist only(Object item1, Object item2) {
return item -> item1.equals(item) || item2.equals(item); return item -> item1.equals(item) || item2.equals(item);
} }
static Whitelist not(Object item1, Object item2) { static Whitelist not(Object item1, Object item2) {
return item -> !(item1.equals(item) || item2.equals(item)); return item -> !(item1.equals(item) || item2.equals(item));
} }
static Whitelist only(Object[] objects) { static Whitelist only(Object[] objects) {
return new SetBasedWhitelist(objects, false); return new SetBasedWhitelist(objects, false);
} }
static Whitelist not(Object[] objects) { static Whitelist not(Object[] objects) {
return new SetBasedWhitelist(objects, true); return new SetBasedWhitelist(objects, true);
} }
static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) { static Whitelist fromConfig(ConfigurationSection section, Function<String, ?> parser) {
if (section == null) { if (section == null) {
return NOTHING; return NOTHING;
} }
boolean blacklist = section.getBoolean("blacklist", false); boolean blacklist = section.getBoolean("blacklist", false);
Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet()); Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet());
switch (list.size()) { switch (list.size()) {
case 0: case 0:
return blacklist ? EVERYTHING : NOTHING; return blacklist ? EVERYTHING : NOTHING;
case 1: { case 1: {
Iterator iterator = list.iterator(); Iterator iterator = list.iterator();
return blacklist ? not(iterator.next()) : only(iterator.next()); return blacklist ? not(iterator.next()) : only(iterator.next());
} }
case 2: { case 2: {
Iterator iterator = list.iterator(); Iterator iterator = list.iterator();
return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next()); return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next());
} }
default: default:
Object item = list.iterator().next(); Object item = list.iterator().next();
if (item instanceof Enum) { if (item instanceof Enum) {
list = EnumSet.copyOf(list); list = EnumSet.copyOf(list);
} }
return new SetBasedWhitelist(list, blacklist); return new SetBasedWhitelist(list, blacklist);
} }
} }
static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) { static void copyIntoConfig(ConfigurationSection target, Function<Object, String> mapper, boolean blacklist, Object... objects) {
target.set("blacklist", blacklist); target.set("blacklist", blacklist);
target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList())); target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList()));
} }
@Override @Override
default boolean test(Object o) { default boolean test(Object o) {
return isWhitelisted(o); return isWhitelisted(o);
} }
boolean isWhitelisted(Object o); boolean isWhitelisted(Object o);
} }

View File

@@ -1,46 +1,46 @@
package io.dico.dicore.event; package io.dico.dicore.event;
import io.dico.dicore.InterfaceChain; import io.dico.dicore.InterfaceChain;
public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> { public interface ChainedListener<T> extends InterfaceChain<SimpleListener<T>, ChainedListener<T>>, SimpleListener<T> {
@Override @Override
default ChainedListener<T> getEmptyInstance() { default ChainedListener<T> getEmptyInstance() {
return ChainedListeners.empty(); return ChainedListeners.empty();
} }
@Override @Override
default ChainedListener<T> withElement(SimpleListener<T> element) { default ChainedListener<T> withElement(SimpleListener<T> element) {
if (element == null) { if (element == null) {
return this; return this;
} }
int count = getElementCount() + 1; int count = getElementCount() + 1;
return new ChainedListener<T>() { return new ChainedListener<T>() {
@Override @Override
public void accept(T event) { public void accept(T event) {
try { try {
ChainedListener.this.accept(event); ChainedListener.this.accept(event);
} finally { } finally {
element.accept(event); element.accept(event);
} }
} }
@Override @Override
public ChainedListener<T> withoutLastNode() { public ChainedListener<T> withoutLastNode() {
return ChainedListener.this; return ChainedListener.this;
} }
@Override @Override
public SimpleListener<T> getDelegateOfLastNode() { public SimpleListener<T> getDelegateOfLastNode() {
return element; return element;
} }
@Override @Override
public int getElementCount() { public int getElementCount() {
return count; return count;
} }
}; };
} }
} }

View File

@@ -1,55 +1,55 @@
package io.dico.dicore.event; package io.dico.dicore.event;
import io.dico.dicore.InterfaceChain; import io.dico.dicore.InterfaceChain;
public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle { public interface ChainedListenerHandle extends InterfaceChain<ListenerHandle, ChainedListenerHandle>, ListenerHandle {
@Override @Override
default ChainedListenerHandle getEmptyInstance() { default ChainedListenerHandle getEmptyInstance() {
return ChainedListenerHandles.empty(); return ChainedListenerHandles.empty();
} }
@Override @Override
default ChainedListenerHandle withElement(ListenerHandle element) { default ChainedListenerHandle withElement(ListenerHandle element) {
if (element == null) { if (element == null) {
return this; return this;
} }
int count = getElementCount() + 1; int count = getElementCount() + 1;
return new ChainedListenerHandle() { return new ChainedListenerHandle() {
@Override @Override
public void register() { public void register() {
try { try {
ChainedListenerHandle.this.register(); ChainedListenerHandle.this.register();
} finally { } finally {
element.register(); element.register();
} }
} }
@Override @Override
public void unregister() { public void unregister() {
try { try {
ChainedListenerHandle.this.unregister(); ChainedListenerHandle.this.unregister();
} finally { } finally {
element.unregister(); element.unregister();
} }
} }
@Override @Override
public ChainedListenerHandle withoutLastNode() { public ChainedListenerHandle withoutLastNode() {
return ChainedListenerHandle.this; return ChainedListenerHandle.this;
} }
@Override @Override
public ListenerHandle getDelegateOfLastNode() { public ListenerHandle getDelegateOfLastNode() {
return element; return element;
} }
@Override @Override
public int getElementCount() { public int getElementCount() {
return count; return count;
} }
}; };
} }
} }

View File

@@ -1,63 +1,63 @@
package io.dico.dicore.event; package io.dico.dicore.event;
public class ChainedListenerHandles { public class ChainedListenerHandles {
private ChainedListenerHandles() { private ChainedListenerHandles() {
} }
private static final ChainedListenerHandle empty = new ChainedListenerHandle() { private static final ChainedListenerHandle empty = new ChainedListenerHandle() {
@Override @Override
public void register() { public void register() {
} }
public void unregister() { public void unregister() {
} }
@Override @Override
public ChainedListenerHandle withElement(ListenerHandle other) { public ChainedListenerHandle withElement(ListenerHandle other) {
return ChainedListenerHandles.singleton(other); return ChainedListenerHandles.singleton(other);
} }
@Override @Override
public int getElementCount() { public int getElementCount() {
return 0; return 0;
} }
@Override @Override
public ListenerHandle getDelegateOfLastNode() { public ListenerHandle getDelegateOfLastNode() {
return null; return null;
} }
}; };
public static ChainedListenerHandle empty() { public static ChainedListenerHandle empty() {
return empty; return empty;
} }
public static ChainedListenerHandle singleton(ListenerHandle element) { public static ChainedListenerHandle singleton(ListenerHandle element) {
if (element instanceof ChainedListenerHandle) { if (element instanceof ChainedListenerHandle) {
return (ChainedListenerHandle) element; return (ChainedListenerHandle) element;
} }
if (element == null) { if (element == null) {
return empty(); return empty();
} }
return new ChainedListenerHandle() { return new ChainedListenerHandle() {
@Override @Override
public void register() { public void register() {
element.register(); element.register();
} }
public void unregister() { public void unregister() {
element.unregister(); element.unregister();
} }
@Override @Override
public ListenerHandle getDelegateOfLastNode() { public ListenerHandle getDelegateOfLastNode() {
return element; return element;
} }
}; };
} }
} }

View File

@@ -1,56 +1,56 @@
package io.dico.dicore.event; package io.dico.dicore.event;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class ChainedListeners { public class ChainedListeners {
private static final ChainedListener<?> empty = new ChainedListener<Object>() { private static final ChainedListener<?> empty = new ChainedListener<Object>() {
@Override @Override
public void accept(Object event) { public void accept(Object event) {
} }
@Override @Override
public ChainedListener<Object> withElement(SimpleListener other) { public ChainedListener<Object> withElement(SimpleListener other) {
return ChainedListeners.singleton(other); return ChainedListeners.singleton(other);
} }
@Override @Override
public int getElementCount() { public int getElementCount() {
return 0; return 0;
} }
@Override @Override
public SimpleListener<Object> getDelegateOfLastNode() { public SimpleListener<Object> getDelegateOfLastNode() {
return null; return null;
} }
}; };
private ChainedListeners() { private ChainedListeners() {
} }
public static <T> ChainedListener<T> empty() { public static <T> ChainedListener<T> empty() {
return (ChainedListener<T>) empty; return (ChainedListener<T>) empty;
} }
public static <T> ChainedListener<T> singleton(SimpleListener<T> element) { public static <T> ChainedListener<T> singleton(SimpleListener<T> element) {
if (element instanceof ChainedListener) { if (element instanceof ChainedListener) {
return (ChainedListener<T>) element; return (ChainedListener<T>) element;
} }
if (element == null) { if (element == null) {
return empty(); return empty();
} }
return new ChainedListener<T>() { return new ChainedListener<T>() {
@Override @Override
public void accept(T event) { public void accept(T event) {
element.accept(event); element.accept(event);
} }
@Override @Override
public SimpleListener getDelegateOfLastNode() { public SimpleListener getDelegateOfLastNode() {
return element; return element;
} }
}; };
} }
} }

View File

@@ -1,91 +1,91 @@
package io.dico.dicore.event; package io.dico.dicore.event;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
public final class HandlerList<T> { public final class HandlerList<T> {
private final List<Listener<T>> source = new ArrayList<>(); private final List<Listener<T>> source = new ArrayList<>();
private Listener<T>[] listeners = newArray(0); private Listener<T>[] listeners = newArray(0);
public void refresh() { public void refresh() {
source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal())); source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal()));
listeners = source.toArray(newArray(source.size())); listeners = source.toArray(newArray(source.size()));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T> Listener<T>[] newArray(int length) { private static <T> Listener<T>[] newArray(int length) {
return new Listener[length]; return new Listener[length];
} }
public void register(Listener<T> listener) { public void register(Listener<T> listener) {
if (!source.contains(listener) && source.add(listener)) { if (!source.contains(listener) && source.add(listener)) {
refresh(); refresh();
} }
} }
public ListenerHandle getListenerHandle(Listener<T> listener) { public ListenerHandle getListenerHandle(Listener<T> listener) {
return new ListenerHandle() { return new ListenerHandle() {
@Override @Override
public void register() { public void register() {
HandlerList.this.register(listener); HandlerList.this.register(listener);
} }
@Override @Override
public void unregister() { public void unregister() {
HandlerList.this.unregister(listener); HandlerList.this.unregister(listener);
} }
}; };
} }
public void register(EventPriority priority, Consumer<T> listener) { public void register(EventPriority priority, Consumer<T> listener) {
register(new Listener<T>() { register(new Listener<T>() {
@Override @Override
public EventPriority getPriority() { public EventPriority getPriority() {
return priority; return priority;
} }
@Override @Override
public void accept(T event) { public void accept(T event) {
listener.accept(event); listener.accept(event);
} }
}); });
} }
public List<Listener<T>> getRegistrations() { public List<Listener<T>> getRegistrations() {
return Collections.unmodifiableList(source); return Collections.unmodifiableList(source);
} }
public void unregister(Listener<T> listener) { public void unregister(Listener<T> listener) {
if (source.remove(listener)) { if (source.remove(listener)) {
refresh(); refresh();
} }
} }
public void callEvent(T event) { public void callEvent(T event) {
if (event instanceof Cancellable) { if (event instanceof Cancellable) {
Cancellable c = (Cancellable) event; Cancellable c = (Cancellable) event;
boolean cancelled = c.isCancelled(); boolean cancelled = c.isCancelled();
for (Listener<T> listener : listeners) { for (Listener<T> listener : listeners) {
if (listener.listensToCancelledState(cancelled)) { if (listener.listensToCancelledState(cancelled)) {
//EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
listener.accept(event); listener.accept(event);
cancelled = c.isCancelled(); cancelled = c.isCancelled();
} /*else { } /*else {
EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName()); EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName());
}*/ }*/
} }
} else { } else {
for (Listener<T> listener : listeners) { for (Listener<T> listener : listeners) {
//EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName());
listener.accept(event); listener.accept(event);
} }
} }
} }
} }

View File

@@ -1,17 +1,17 @@
package io.dico.dicore.event; package io.dico.dicore.event;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
public interface Listener<T> { public interface Listener<T> {
default EventPriority getPriority() { default EventPriority getPriority() {
return EventPriority.NORMAL; return EventPriority.NORMAL;
} }
default boolean listensToCancelledState(boolean cancelled) { default boolean listensToCancelledState(boolean cancelled) {
return !cancelled; return !cancelled;
} }
void accept(T event); void accept(T event);
} }

View File

@@ -1,9 +1,9 @@
package io.dico.dicore.event; package io.dico.dicore.event;
public interface ListenerHandle { public interface ListenerHandle {
void register(); void register();
void unregister(); void unregister();
} }

Some files were not shown because too many files have changed in this diff Show More