From 0f196f59c6a4cb76ab8409da62ff1f35505f94a8 Mon Sep 17 00:00:00 2001 From: Dico Karssiens Date: Sun, 11 Nov 2018 14:06:45 +0000 Subject: Changes I made before breaking my local repository. Hoping this works. --- .editorconfig | 38 +- .gitignore | 3 +- README.md | 32 +- build.gradle.kts | 22 +- .../dico/dicore/command/ChildCommandAddress.java | 240 +-- .../main/java/io/dico/dicore/command/Command.java | 316 ++-- .../io/dico/dicore/command/CommandBuilder.java | 864 +++++------ .../io/dico/dicore/command/CommandException.java | 56 +- .../java/io/dico/dicore/command/EMessageType.java | 38 +- .../io/dico/dicore/command/EOverridePolicy.java | 24 +- .../io/dico/dicore/command/ExecutionContext.java | 770 +++++----- .../io/dico/dicore/command/ExtendedCommand.java | 128 +- .../io/dico/dicore/command/ICommandAddress.java | 410 ++--- .../io/dico/dicore/command/ICommandDispatcher.java | 292 ++-- .../io/dico/dicore/command/IContextFilter.java | 402 ++--- .../dicore/command/InheritingContextFilter.java | 128 +- .../java/io/dico/dicore/command/LambdaCommand.java | 70 +- .../dicore/command/ModifiableCommandAddress.java | 624 ++++---- .../dicore/command/PermissionContextFilter.java | 272 ++-- .../io/dico/dicore/command/RootCommandAddress.java | 550 +++---- .../main/java/io/dico/dicore/command/Validate.java | 104 +- .../dico/dicore/command/annotation/BigRange.java | 104 +- .../io/dico/dicore/command/annotation/Cmd.java | 32 +- .../dicore/command/annotation/CmdParamType.java | 54 +- .../command/annotation/CommandAnnotationUtils.java | 70 +- .../io/dico/dicore/command/annotation/Desc.java | 54 +- .../io/dico/dicore/command/annotation/Flag.java | 32 +- .../command/annotation/GenerateCommands.java | 28 +- .../command/annotation/GroupMatchedCommands.java | 136 +- .../dico/dicore/command/annotation/NamedArg.java | 28 +- .../dicore/command/annotation/PreprocessArgs.java | 34 +- .../io/dico/dicore/command/annotation/Range.java | 134 +- .../dicore/command/annotation/RequireConsole.java | 22 +- .../command/annotation/RequireParameters.java | 28 +- .../command/annotation/RequirePermissions.java | 66 +- .../dicore/command/annotation/RequirePlayer.java | 22 +- .../dicore/command/chat/AbstractChatHandler.java | 188 +-- .../io/dico/dicore/command/chat/ChatHandlers.java | 40 +- .../io/dico/dicore/command/chat/IChatHandler.java | 120 +- .../dico/dicore/command/chat/help/HelpPages.java | 184 +-- .../command/chat/help/HelpTopicModifier.java | 48 +- .../dicore/command/chat/help/IHelpComponent.java | 18 +- .../dico/dicore/command/chat/help/IHelpTopic.java | 46 +- .../dico/dicore/command/chat/help/IPageBorder.java | 14 +- .../dicore/command/chat/help/IPageBuilder.java | 26 +- .../dico/dicore/command/chat/help/IPageLayout.java | 40 +- .../dico/dicore/command/chat/help/PageBorders.java | 152 +- .../command/chat/help/SimpleHelpComponent.java | 54 +- .../chat/help/defaults/DefaultPageBuilder.java | 230 +-- .../chat/help/defaults/DefaultPageLayout.java | 80 +- .../chat/help/defaults/DescriptionHelpTopic.java | 90 +- .../chat/help/defaults/SubcommandsHelpTopic.java | 118 +- .../chat/help/defaults/SyntaxHelpTopic.java | 184 +-- .../chat/help/insertion/EInsertionStage.java | 58 +- .../chat/help/insertion/HelpComponentInserter.java | 86 +- .../command/chat/help/insertion/IInsertion.java | 14 +- .../chat/help/insertion/IInsertionFunction.java | 28 +- .../command/chat/help/insertion/Insertions.java | 62 +- .../dicore/command/parameter/ArgumentBuffer.java | 590 ++++---- .../parameter/ArgumentMergingPreProcessor.java | 354 ++--- .../dicore/command/parameter/ContextParser.java | 552 +++---- .../command/parameter/IArgumentPreProcessor.java | 82 +- .../dicore/command/parameter/ParameterList.java | 296 ++-- .../command/parameter/type/EnumParameterType.java | 92 +- .../parameter/type/IParameterTypeSelector.java | 88 +- .../type/MapBasedParameterTypeSelector.java | 228 +-- .../command/parameter/type/ParameterConfig.java | 160 +- .../command/parameter/type/ParameterType.java | 298 ++-- .../parameter/type/SimpleParameterType.java | 62 +- .../dicore/command/predef/DefaultGroupCommand.java | 112 +- .../io/dico/dicore/command/predef/HelpCommand.java | 152 +- .../dicore/command/predef/PredefinedCommand.java | 100 +- .../dico/dicore/command/predef/SyntaxCommand.java | 72 +- .../dicore/command/registration/BukkitCommand.java | 244 +-- .../dicore/command/registration/CommandMap.java | 118 +- .../reflect/CommandParseException.java | 54 +- .../registration/reflect/ICommandInterceptor.java | 72 +- .../registration/reflect/ICommandReceiver.java | 10 +- .../registration/reflect/ReflectiveCommand.java | 374 ++--- .../reflect/ReflectiveRegistration.java | 830 +++++------ .../reflect/KotlinReflectiveRegistration.kt | 132 +- .../example/ParameterInfoObjectExample.java | 146 +- .../src/main/java/io/dico/dicore/BitModifier.java | 364 ++--- .../src/main/java/io/dico/dicore/Formatting.java | 588 ++++---- .../main/java/io/dico/dicore/InterfaceChain.java | 346 ++--- .../java/io/dico/dicore/InventoryEventUtil.java | 240 +-- .../core/src/main/java/io/dico/dicore/Logging.java | 232 +-- .../src/main/java/io/dico/dicore/Reflection.java | 1560 ++++++++++---------- .../java/io/dico/dicore/SetBasedWhitelist.java | 118 +- .../src/main/java/io/dico/dicore/SpigotUtil.java | 816 +++++----- .../src/main/java/io/dico/dicore/StringUtil.java | 946 ++++++------ .../src/main/java/io/dico/dicore/Whitelist.java | 182 +-- .../java/io/dico/dicore/event/ChainedListener.java | 92 +- .../dico/dicore/event/ChainedListenerHandle.java | 110 +- .../dico/dicore/event/ChainedListenerHandles.java | 126 +- .../io/dico/dicore/event/ChainedListeners.java | 112 +- .../java/io/dico/dicore/event/HandlerList.java | 182 +-- .../main/java/io/dico/dicore/event/Listener.java | 34 +- .../java/io/dico/dicore/event/ListenerHandle.java | 18 +- .../java/io/dico/dicore/event/SimpleListener.java | 14 +- .../dico/dicore/exceptions/ExceptionHandler.java | 406 ++--- .../checkedfunctions/CheckedBiConsumer.java | 138 +- .../checkedfunctions/CheckedBiFunction.java | 152 +- .../checkedfunctions/CheckedConsumer.java | 134 +- .../checkedfunctions/CheckedFunction.java | 148 +- .../checkedfunctions/CheckedFunctionalObject.java | 170 +-- .../checkedfunctions/CheckedRunnable.java | 124 +- .../checkedfunctions/CheckedSupplier.java | 140 +- .../main/java/io/dico/dicore/task/BaseTask.java | 216 +-- .../java/io/dico/dicore/task/IteratorTask.java | 240 +-- .../main/java/io/dico/dicore/task/VoidTask.java | 38 +- gradle/wrapper/gradle-wrapper.properties | 10 +- gradlew | 344 ++--- gradlew.bat | 168 +-- javaStyle.xml | 76 +- settings.gradle.kts | 3 +- src/main/kotlin/io/dico/parcels2/Interactable.kt | 346 ++--- src/main/kotlin/io/dico/parcels2/JobDispatcher.kt | 674 ++++----- src/main/kotlin/io/dico/parcels2/Parcel.kt | 132 +- .../kotlin/io/dico/parcels2/ParcelGenerator.kt | 206 +-- src/main/kotlin/io/dico/parcels2/ParcelId.kt | 112 +- src/main/kotlin/io/dico/parcels2/ParcelWorld.kt | 206 +-- src/main/kotlin/io/dico/parcels2/ParcelsPlugin.kt | 304 ++-- src/main/kotlin/io/dico/parcels2/PlayerProfile.kt | 366 ++--- src/main/kotlin/io/dico/parcels2/Privilege.kt | 250 ++-- src/main/kotlin/io/dico/parcels2/Privileges.kt | 128 +- .../io/dico/parcels2/blockvisitor/Attachables.kt | 122 +- .../dico/parcels2/blockvisitor/ExtraBlockChange.kt | 76 +- .../dico/parcels2/blockvisitor/RegionTraverser.kt | 646 ++++---- .../io/dico/parcels2/blockvisitor/Schematic.kt | 242 +-- .../parcels2/command/AbstractParcelCommands.kt | 126 +- .../io/dico/parcels2/command/CommandsAdmin.kt | 188 +-- .../command/CommandsAdminPrivilegesGlobal.kt | 262 ++-- .../io/dico/parcels2/command/CommandsDebug.kt | 322 ++-- .../io/dico/parcels2/command/CommandsGeneral.kt | 282 ++-- .../parcels2/command/CommandsPrivilegesGlobal.kt | 152 +- .../parcels2/command/CommandsPrivilegesLocal.kt | 286 ++-- .../dico/parcels2/command/ParcelCommandBuilder.kt | 274 ++-- .../parcels2/command/ParcelCommandReceivers.kt | 136 +- .../command/ParcelOptionsInteractCommand.kt | 112 +- .../dico/parcels2/command/ParcelParameterTypes.kt | 166 +-- .../io/dico/parcels2/command/ParcelTarget.kt | 382 ++--- .../io/dico/parcels2/command/ParcelsChatHandler.kt | 46 +- .../parcels2/defaultimpl/DefaultParcelContainer.kt | 144 +- .../parcels2/defaultimpl/DefaultParcelGenerator.kt | 754 +++++----- .../defaultimpl/GlobalPrivilegesManagerImpl.kt | 54 +- .../io/dico/parcels2/defaultimpl/InfoBuilder.kt | 174 +-- .../parcels2/defaultimpl/ParcelProviderImpl.kt | 444 +++--- .../dico/parcels2/defaultimpl/ParcelWorldImpl.kt | 150 +- .../dico/parcels2/listener/ParcelEntityTracker.kt | 120 +- .../io/dico/parcels2/listener/ParcelListeners.kt | 1320 ++++++++--------- .../io/dico/parcels2/listener/WorldEditListener.kt | 156 +- .../io/dico/parcels2/options/GeneratorOptions.kt | 70 +- .../io/dico/parcels2/options/MigrationOptions.kt | 42 +- .../kotlin/io/dico/parcels2/options/Options.kt | 114 +- .../io/dico/parcels2/options/OptionsMapper.kt | 132 +- .../io/dico/parcels2/options/PolymorphicOptions.kt | 176 +-- .../io/dico/parcels2/options/StorageOptions.kt | 116 +- .../kotlin/io/dico/parcels2/storage/Backing.kt | 138 +- .../io/dico/parcels2/storage/DataConverters.kt | 74 +- src/main/kotlin/io/dico/parcels2/storage/Hikari.kt | 146 +- .../kotlin/io/dico/parcels2/storage/Storage.kt | 228 +-- .../parcels2/storage/exposed/ExposedBacking.kt | 564 +++---- .../parcels2/storage/exposed/ExposedExtensions.kt | 148 +- .../io/dico/parcels2/storage/exposed/IdTables.kt | 328 ++-- .../io/dico/parcels2/storage/exposed/ListTables.kt | 222 +-- .../dico/parcels2/storage/migration/Migration.kt | 18 +- .../storage/migration/plotme/PlotmeMigration.kt | 234 +-- .../storage/migration/plotme/PlotmeTables.kt | 62 +- .../kotlin/io/dico/parcels2/util/BukkitUtil.kt | 28 +- .../io/dico/parcels2/util/MainThreadDispatcher.kt | 84 +- .../io/dico/parcels2/util/PluginScheduler.kt | 40 +- .../kotlin/io/dico/parcels2/util/ext/Material.kt | 214 +-- src/main/kotlin/io/dico/parcels2/util/ext/Misc.kt | 162 +- .../kotlin/io/dico/parcels2/util/ext/Player.kt | 114 +- .../kotlin/io/dico/parcels2/util/math/Dimension.kt | 36 +- src/main/kotlin/io/dico/parcels2/util/math/Math.kt | 82 +- .../kotlin/io/dico/parcels2/util/math/Region.kt | 72 +- .../kotlin/io/dico/parcels2/util/math/Vec2i.kt | 18 +- .../kotlin/io/dico/parcels2/util/math/Vec3d.kt | 104 +- .../kotlin/io/dico/parcels2/util/math/Vec3i.kt | 210 +-- src/main/resources/logback.xml | 32 +- src/main/resources/plugin.yml | 10 +- todo.md | 206 +-- 184 files changed, 17998 insertions(+), 17998 deletions(-) diff --git a/.editorconfig b/.editorconfig index 32f5e31..169357e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,19 +1,19 @@ -[*] -charset=utf-8 -end_of_line=lf -insert_final_newline=false -indent_style=space -indent_size=4 - -[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] -indent_style=space -indent_size=2 - -[{*.ddl,*.sql}] -indent_style=space -indent_size=2 - -[{*.yml,*.yaml}] -indent_style=space -indent_size=2 - +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=false +indent_style=space +indent_size=4 + +[{.babelrc,.stylelintrc,jest.config,.eslintrc,*.bowerrc,*.jsb3,*.jsb2,*.json}] +indent_style=space +indent_size=2 + +[{*.ddl,*.sql}] +indent_style=space +indent_size=2 + +[{*.yml,*.yaml}] +indent_style=space +indent_size=2 + diff --git a/.gitignore b/.gitignore index 601cc7d..60f7508 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ build/ /debug/ target/ /gradle-output.txt -/*.java \ No newline at end of file +/*.java +*.dump \ No newline at end of file diff --git a/README.md b/README.md index e5aa074..dfebe9c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# Parcels2 - -Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core). - -Newer version of discontinued [Parcels](https://github.com/RedstonerServer/Parcels). - -Written in Kotlin. -This project is WIP. - -![world screenshot](https://i.imgur.com/tpbKrQI.png) - -## Build - -1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory -2. Run `gradle releaseJar` -3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory +# Parcels2 + +Plot management and world generator plugin inspired by [PlotMe](https://github.com/WorldCretornica/PlotMe-Core). + +Newer version of discontinued [Parcels](https://github.com/RedstonerServer/Parcels). + +Written in Kotlin. +This project is WIP. + +![world screenshot](https://i.imgur.com/tpbKrQI.png) + +## Build + +1. Add `worldedit-bukkit-7.0.0-beta-01.jar` to `/debug/plugins` directory +2. Run `gradle releaseJar` +3. Kotlin stdlib classpath is placed in `/debug/lib` directory and artifact can be found in `/debug/plugins` directory diff --git a/build.gradle.kts b/build.gradle.kts index 12c2d97..211a978 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,22 +13,22 @@ version = "0.2" plugins { java - kotlin("jvm") version "1.3.0-rc-57" + kotlin("jvm") version "1.3.0-rc-146" id("com.github.johnrengelman.plugin-shadow") version "2.0.3" } - allprojects { apply() apply(plugin = "idea") repositories { mavenCentral() - maven("https://hub.spigotmc.org/nexus/content/repositories/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/kotlin-eap") + maven("https://hub.spigotmc.org/nexus/content/repositories/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/kotlin-dev/") + maven("https://dl.bintray.com/kotlin/kotlin-eap/") maven("https://dl.bintray.com/kotlin/kotlinx/") } @@ -146,11 +146,6 @@ tasks { val jarUrl = URL("https://yivesmirror.com/files/spigot/spigot-latest.jar") val serverJarFile = file("$serverDir/lib/spigot.jar") - - - doFirst { - - } } } @@ -171,5 +166,8 @@ val ConfigurationContainer.`provided`: Configuration val ConfigurationContainer.`kotlinStd`: Configuration get() = findByName("kotlinStd") ?: create("kotlinStd").let { compileClasspath.extendsFrom(it) } -fun Jar.fromFiles(files: Iterable) = +fun Jar.fromFiles(files: Iterable) { + return afterEvaluate { from(*files.map { if (it.isDirectory) it else zipTree(it) }.toTypedArray()) } +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java index 9a26f61..72bc303 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ChildCommandAddress.java @@ -1,120 +1,120 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.predef.DefaultGroupCommand; -import io.dico.dicore.command.predef.HelpCommand; - -import java.util.*; - -public class ChildCommandAddress extends ModifiableCommandAddress { - ModifiableCommandAddress parent; - final List namesModifiable = new ArrayList<>(4); - List names = namesModifiable; - Command command; - boolean isCommandTrailing; - - public ChildCommandAddress() { - } - - public ChildCommandAddress(Command command) { - this.command = command; - } - - public ChildCommandAddress(Command command, String name, String... aliases) { - this(command); - addNameAndAliases(name, aliases); - } - - public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) { - ChildCommandAddress rv = new ChildCommandAddress(); - rv.setupAsPlaceholder(name, aliases); - return rv; - } - - public void setupAsPlaceholder(String name, String... aliases) { - if (!hasCommand()) { - setCommand(DefaultGroupCommand.getInstance()); - } - - addNameAndAliases(name, aliases); - HelpCommand.registerAsChild(this); - } - - @Override - public boolean isRoot() { - return false; - } - - @Override - public ModifiableCommandAddress getParent() { - return parent; - } - - @Override - public Command getCommand() { - return command; - } - - @Override - public void setCommand(Command command) { - if (hasUserDeclaredCommand()) { - throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\""); - } - this.command = command; - } - - @Override - public List getNames() { - return names; - } - - public void addNameAndAliases(String name, String... aliases) { - names.add(name); - names.addAll(Arrays.asList(aliases)); - } - - @Override - public String getMainKey() { - return namesModifiable.isEmpty() ? null : namesModifiable.get(0); - } - - @Override - public String getAddress() { - ICommandAddress address = this; - int depth = getDepth(); - String[] keys = new String[depth]; - for (int i = depth - 1; i >= 0; i--) { - keys[i] = address.getMainKey(); - address = address.getParent(); - } - return String.join(" ", keys); - } - - public void finalizeNames() { - if (names == namesModifiable) { - names = Collections.unmodifiableList(namesModifiable); - } - } - - Iterator modifiableNamesIterator() { - return namesModifiable.iterator(); - } - - void setParent(ModifiableCommandAddress parent) { - finalizeNames(); - this.parent = parent; - } - - @Override - public boolean isCommandTrailing() { - return isCommandTrailing; - } - - @Override - public void setCommandTrailing(boolean trailing) { - if (hasChildren()) { - throw new IllegalStateException("Address already has children, this property can't be modified"); - } - isCommandTrailing = trailing; - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.predef.DefaultGroupCommand; +import io.dico.dicore.command.predef.HelpCommand; + +import java.util.*; + +public class ChildCommandAddress extends ModifiableCommandAddress { + ModifiableCommandAddress parent; + final List namesModifiable = new ArrayList<>(4); + List names = namesModifiable; + Command command; + boolean isCommandTrailing; + + public ChildCommandAddress() { + } + + public ChildCommandAddress(Command command) { + this.command = command; + } + + public ChildCommandAddress(Command command, String name, String... aliases) { + this(command); + addNameAndAliases(name, aliases); + } + + public static ChildCommandAddress newPlaceHolderCommand(String name, String... aliases) { + ChildCommandAddress rv = new ChildCommandAddress(); + rv.setupAsPlaceholder(name, aliases); + return rv; + } + + public void setupAsPlaceholder(String name, String... aliases) { + if (!hasCommand()) { + setCommand(DefaultGroupCommand.getInstance()); + } + + addNameAndAliases(name, aliases); + HelpCommand.registerAsChild(this); + } + + @Override + public boolean isRoot() { + return false; + } + + @Override + public ModifiableCommandAddress getParent() { + return parent; + } + + @Override + public Command getCommand() { + return command; + } + + @Override + public void setCommand(Command command) { + if (hasUserDeclaredCommand()) { + throw new IllegalStateException("Command is already set at address \"" + getAddress() + "\""); + } + this.command = command; + } + + @Override + public List getNames() { + return names; + } + + public void addNameAndAliases(String name, String... aliases) { + names.add(name); + names.addAll(Arrays.asList(aliases)); + } + + @Override + public String getMainKey() { + return namesModifiable.isEmpty() ? null : namesModifiable.get(0); + } + + @Override + public String getAddress() { + ICommandAddress address = this; + int depth = getDepth(); + String[] keys = new String[depth]; + for (int i = depth - 1; i >= 0; i--) { + keys[i] = address.getMainKey(); + address = address.getParent(); + } + return String.join(" ", keys); + } + + public void finalizeNames() { + if (names == namesModifiable) { + names = Collections.unmodifiableList(namesModifiable); + } + } + + Iterator modifiableNamesIterator() { + return namesModifiable.iterator(); + } + + void setParent(ModifiableCommandAddress parent) { + finalizeNames(); + this.parent = parent; + } + + @Override + public boolean isCommandTrailing() { + return isCommandTrailing; + } + + @Override + public void setCommandTrailing(boolean trailing) { + if (hasChildren()) { + throw new IllegalStateException("Address already has children, this property can't be modified"); + } + isCommandTrailing = trailing; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java index 894e74e..97f4b78 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/Command.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/Command.java @@ -1,158 +1,158 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.IContextFilter.Priority; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.IArgumentPreProcessor; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import io.dico.dicore.command.parameter.type.ParameterType; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -public abstract class Command { - private static final String[] EMPTY_DESCRIPTION = new String[0]; - private final ParameterList parameterList = new ParameterList(); - private final List contextFilters = new ArrayList<>(3); - private String[] description = EMPTY_DESCRIPTION; - private String shortDescription; - - public Command addParameter(Parameter parameter) { - parameterList.addParameter(parameter); - return this; - } - - public Command addParameter(String name, String description, ParameterType type) { - return addParameter(new Parameter<>(name, description, type, null, false, null)); - } - - public Command addParameter(String name, String description, ParameterType type, TParamInfo paramInfo) { - return addParameter(new Parameter<>(name, description, type, paramInfo, false, null)); - } - - public Command addFlag(String name, String description, ParameterType type) { - return addParameter(new Parameter<>('-' + name, description, type, null, true, null)); - } - - public Command addFlag(String name, String description, ParameterType type, TParamInfo paramInfo) { - return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null)); - } - - public Command addAuthorizedFlag(String name, String description, ParameterType type, String permission) { - return addParameter(new Parameter<>('-' + name, description, type, null, true, permission)); - } - - public Command addAuthorizedFlag(String name, String description, ParameterType type, TParamInfo paramInfo, String permission) { - return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission)); - } - - public Command requiredParameters(int requiredParameters) { - parameterList.setRequiredCount(requiredParameters); - return this; - } - - public Command repeatFinalParameter() { - parameterList.setRepeatFinalParameter(true); - return this; - } - - public Command setDescription(String... description) { - this.description = Objects.requireNonNull(description); - return this; - } - - public Command setShortDescription(String shortDescription) { - this.shortDescription = shortDescription; - return this; - } - - /* - public Command preprocessArguments(IArgumentPreProcessor processor) { - parameterList.setArgumentPreProcessor(processor); - return this; - }*/ - - public final ParameterList getParameterList() { - return parameterList; - } - - public final String[] getDescription() { - return description.length == 0 ? description : description.clone(); - } - - public String getShortDescription() { - return shortDescription; - } - - /** - * ---- CONTEXT FILTERS ---- - * Filter the contexts. For example, if the sender must be a player but it's the console, - * throw a CommandException describing the problem. - */ - private transient int postParameterFilterCount = 0; - - public Command addContextFilter(IContextFilter contextFilter) { - Objects.requireNonNull(contextFilter); - if (!contextFilters.contains(contextFilter)) { - contextFilters.add(contextFilter); - contextFilters.sort(null); - - if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { - postParameterFilterCount++; - } - } - return this; - } - - public List getContextFilters() { - return Collections.unmodifiableList(contextFilters); - } - - public Command removeContextFilter(IContextFilter contextFilter) { - boolean ret = contextFilters.remove(contextFilter); - if (ret) { - if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { - postParameterFilterCount--; - } - } - return this; - } - - // ---- CONTROL FLOW IN COMMAND TREES ---- - - public boolean isVisibleTo(CommandSender sender) { - return true; - } - - public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) { - return false; - } - - // ---- EXECUTION ---- - - public void initializeAndFilterContext(ExecutionContext context) throws CommandException { - int i, n; - for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) { - contextFilters.get(i).filterContext(context); - } - - context.parse(parameterList); - - if (!context.isTabComplete()) { - for (n = contextFilters.size(); i < n; i++) { - contextFilters.get(i).filterContext(context); - } - } - } - - public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException; - - public List tabComplete(CommandSender sender, ExecutionContext context, Location location) { - return context.getSuggestedCompletions(location); - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.IContextFilter.Priority; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.IArgumentPreProcessor; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.ParameterList; +import io.dico.dicore.command.parameter.type.ParameterType; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public abstract class Command { + private static final String[] EMPTY_DESCRIPTION = new String[0]; + private final ParameterList parameterList = new ParameterList(); + private final List contextFilters = new ArrayList<>(3); + private String[] description = EMPTY_DESCRIPTION; + private String shortDescription; + + public Command addParameter(Parameter parameter) { + parameterList.addParameter(parameter); + return this; + } + + public Command addParameter(String name, String description, ParameterType type) { + return addParameter(new Parameter<>(name, description, type, null, false, null)); + } + + public Command addParameter(String name, String description, ParameterType type, TParamInfo paramInfo) { + return addParameter(new Parameter<>(name, description, type, paramInfo, false, null)); + } + + public Command addFlag(String name, String description, ParameterType type) { + return addParameter(new Parameter<>('-' + name, description, type, null, true, null)); + } + + public Command addFlag(String name, String description, ParameterType type, TParamInfo paramInfo) { + return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, null)); + } + + public Command addAuthorizedFlag(String name, String description, ParameterType type, String permission) { + return addParameter(new Parameter<>('-' + name, description, type, null, true, permission)); + } + + public Command addAuthorizedFlag(String name, String description, ParameterType type, TParamInfo paramInfo, String permission) { + return addParameter(new Parameter<>('-' + name, description, type, paramInfo, true, permission)); + } + + public Command requiredParameters(int requiredParameters) { + parameterList.setRequiredCount(requiredParameters); + return this; + } + + public Command repeatFinalParameter() { + parameterList.setRepeatFinalParameter(true); + return this; + } + + public Command setDescription(String... description) { + this.description = Objects.requireNonNull(description); + return this; + } + + public Command setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + return this; + } + + /* + public Command preprocessArguments(IArgumentPreProcessor processor) { + parameterList.setArgumentPreProcessor(processor); + return this; + }*/ + + public final ParameterList getParameterList() { + return parameterList; + } + + public final String[] getDescription() { + return description.length == 0 ? description : description.clone(); + } + + public String getShortDescription() { + return shortDescription; + } + + /** + * ---- CONTEXT FILTERS ---- + * Filter the contexts. For example, if the sender must be a player but it's the console, + * throw a CommandException describing the problem. + */ + private transient int postParameterFilterCount = 0; + + public Command addContextFilter(IContextFilter contextFilter) { + Objects.requireNonNull(contextFilter); + if (!contextFilters.contains(contextFilter)) { + contextFilters.add(contextFilter); + contextFilters.sort(null); + + if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { + postParameterFilterCount++; + } + } + return this; + } + + public List getContextFilters() { + return Collections.unmodifiableList(contextFilters); + } + + public Command removeContextFilter(IContextFilter contextFilter) { + boolean ret = contextFilters.remove(contextFilter); + if (ret) { + if (contextFilter.getPriority().compareTo(Priority.POST_PARAMETERS) >= 0) { + postParameterFilterCount--; + } + } + return this; + } + + // ---- CONTROL FLOW IN COMMAND TREES ---- + + public boolean isVisibleTo(CommandSender sender) { + return true; + } + + public boolean takePrecedenceOverSubcommand(String subCommand, ArgumentBuffer buffer) { + return false; + } + + // ---- EXECUTION ---- + + public void initializeAndFilterContext(ExecutionContext context) throws CommandException { + int i, n; + for (i = 0, n = contextFilters.size() - postParameterFilterCount; i < n; i++) { + contextFilters.get(i).filterContext(context); + } + + context.parse(parameterList); + + if (!context.isTabComplete()) { + for (n = contextFilters.size(); i < n; i++) { + contextFilters.get(i).filterContext(context); + } + } + } + + public abstract String execute(CommandSender sender, ExecutionContext context) throws CommandException; + + public List tabComplete(CommandSender sender, ExecutionContext context, Location location) { + return context.getSuggestedCompletions(location); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java index e527f27..118dd0f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandBuilder.java @@ -1,432 +1,432 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; -import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; -import io.dico.dicore.command.parameter.type.ParameterType; -import io.dico.dicore.command.predef.HelpCommand; -import io.dico.dicore.command.predef.PredefinedCommand; -import io.dico.dicore.command.predef.SyntaxCommand; -import io.dico.dicore.command.registration.reflect.ReflectiveRegistration; - -import java.util.HashSet; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Mimic of WorldEdit's CommandGraph - */ -public final class CommandBuilder { - private final RootCommandAddress root; - private ModifiableCommandAddress cur; - private IParameterTypeSelector selector; - - /** - * Instantiate a new CommandBuilder with a new command root system - * Commands registered to this command builder might interfere with - * commands registered to other commands builders or by other plugins. - */ - public CommandBuilder() { - this(new RootCommandAddress()); - } - - /** - * Instantiate a new CommandBuilder with a specified root address. - * If the root address is identical to that of another command builder, - * they will modify the same tree. - * - * @param root the root address - */ - public CommandBuilder(RootCommandAddress root) { - this.root = Objects.requireNonNull(root); - this.cur = root; - this.selector = new MapBasedParameterTypeSelector(true); - } - - /** - * Add a sub command at the current address - * The current address can be inspected using {@link #getAddress()} - * - * @param name the name of the command - * @param command the command executor - * @param aliases any aliases - * @return this - */ - public CommandBuilder addSubCommand(String name, Command command, String... aliases) { - ChildCommandAddress address = new ChildCommandAddress(command); - address.addNameAndAliases(name, aliases); - return addSubCommand(address); - } - - /** - * Add a subcommand as an address at the current address - * The result of this call is the same as - * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))} - * - * @param address the address - * @return this - * @throws IllegalArgumentException if {@code address.isRoot()} - */ - public CommandBuilder addSubCommand(ICommandAddress address) { - cur.addChild(address); - return this; - } - - /** - * Search the given class for any (static) methods using command annotations - * 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. - * - * @param clazz the clazz - * @return this - * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class - * @see #registerCommands(Class, Object) - */ - public CommandBuilder registerCommands(Class clazz) { - return registerCommands(clazz, null); - } - - /** - * 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. - * 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. - * - * @param object the object - * @return this - * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class - * @see #registerCommands(Class, Object) - */ - public CommandBuilder registerCommands(Object object) { - return registerCommands(object.getClass(), object); - } - - /** - * Search the given class for methods using command annotations. - * 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. - * The instance is used to invoke non-static methods. - * - * @param clazz the class - * @param instance the instance, null if only static methods - * @return this - * @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 - */ - public CommandBuilder registerCommands(Class clazz, Object instance) { - try { - ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance); - return this; - } catch (Exception ex) { - throw new IllegalArgumentException(ex); - } - } - - /** - * register the {@link HelpCommand} as a sub command at the current address - * - * @return this - */ - public CommandBuilder registerHelpCommand() { - HelpCommand.registerAsChild(cur); - return this; - } - - /** - * register the {@link SyntaxCommand} as a sub command a the current address - * - * @return this - */ - public CommandBuilder registerSyntaxCommand() { - SyntaxCommand.registerAsChild(cur); - return this; - } - - /** - * Generate the predefined commands. - * These are presets. - * Examples include {@code help} and {@code syntax}. - *

- * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)} - * - * @param commands the commands - * @return this - */ - public CommandBuilder generatePredefinedCommands(String... commands) { - ReflectiveRegistration.generateCommands(cur, commands); - return this; - } - - /** - * Unregister any childs present at the given keys. - *

- * 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 - * registered by {@link #registerCommands(Class, Object)}, or because you didn't - * 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 keys a varargs array containing the keys - * @return this - * @throws IllegalArgumentException if keys array is empty - */ - public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) { - cur.removeChildren(removeAliases, keys); - return this; - } - - /** - * Jump to the sub-address with the given name as main key. - * If an address with the exact name as main key exists, - * that address becomes the current address. - *

- * Otherwise, a new addresses is registered with the name and aliases. - * New addresses registered by this command have a HelpCommand added by default. - *

- * 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()} - * should be made. - *

- * If the address is the target of a command, it will provide information about its sub commands - * using the HelpCommand. - * - * @param name the main key - * @param aliases the aliases - * @return this - */ - public CommandBuilder group(String name, String... aliases) { - ChildCommandAddress address = cur.getChild(name); - if (address == null || !name.equals(address.getMainKey())) { - address = new ChildCommandAddress(); - address.setupAsPlaceholder(name, aliases); - cur.addChild(address); - } - cur = address; - return this; - } - - /** - * 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}. - * - *

The address must not have a parent or any keys

- * - * @param address the address object to use - * @param name the main key - * @param aliases any aliases - * @return this - * @throws IllegalArgumentException if any of the requirements set out above aren't met - */ - public CommandBuilder group(ICommandAddress address, String name, String... aliases) { - if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) { - throw new IllegalArgumentException(); - } - - ChildCommandAddress asChild = (ChildCommandAddress) address; - asChild.setupAsPlaceholder(name, aliases); - cur.addChild(address); - cur = asChild; - return this; - } - - /** - * 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...)} - * - * @param shortDescription a short description - * @param description the lines of a full description. - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder setGroupDescription(String shortDescription, String... description) { - Command command = cur.getCommand(); - if (command == null) throw new IllegalStateException(); - cur.setCommand(command - .setShortDescription(shortDescription) - .setDescription(description)); - return this; - } - - /** - * Add a context filter to the command of the current group - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder addContextFilter(IContextFilter contextFilter) { - Command command = cur.getCommand(); - if (command == null) throw new IllegalStateException(); - cur.setCommand(command - .addContextFilter(contextFilter)); - return this; - } - - /** - * Add a required permission to the command of the current group - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder addPermission(String permission) { - return addContextFilter(IContextFilter.permission(permission)); - } - - /** - * Add a required permission to the command of the current group, which can be inherited - * @return this - * @throws IllegalStateException if the current group has no command - */ - public CommandBuilder addInheritablePermission(String permission) { - return addContextFilter(IContextFilter.inheritablePermission(permission)); - } - - /** - * Jump up a level in the address - * - * @return this - * @throws IllegalStateException if the address is empty - * // has a depth of 0 // is at level 0 - */ - public CommandBuilder parent() { - if (cur.hasParent()) { - cur = cur.getParent(); - return this; - } - throw new IllegalStateException("No parent exists at this address"); - } - - /** - * Jump to the root (empty) address, - * such that a subsequent call to {@link #parent()} - * will throw a {@link IllegalStateException} - * - * @return this - */ - public CommandBuilder root() { - cur = root; - return this; - } - - /** - * Get the current address, as a space-separated string - * - * @return the current address - */ - public String getAddress() { - return cur.getAddress(); - } - - /** - * Get the depth of the current address. - * This is equivalent to {@code getAddress().split(" ").length}. - * If the address is empty, the depth is 0. - * - * @return the depth - */ - public int getDepth() { - return cur.getDepth(); - } - - /** - * 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...)} - *

- * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before, - * the description is copied to the given executor. - * - * @param command the executor - * @return this - * @throws IllegalArgumentException if the command at the address is present and declared by the user, - * in other words, it's not a {@link PredefinedCommand} - */ - public CommandBuilder setCommand(Command command) { - Command current = cur.getCommand(); - if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) { - command.setShortDescription(current.getShortDescription()); - command.setDescription(current.getDescription()); - } - - cur.setCommand(command); - return this; - } - - /** - * Configure the chat handler at this address. The chat handler - * is used for all children down the tree if they don't explicitly have - * their own chat handler configured. If this isn't configured, - * {@code ChatHandlers.defaultChat()} is used. - * - * @param chatHandler the chat handler - * @return this - */ - public CommandBuilder setChatHandler(IChatHandler chatHandler) { - cur.setChatHandler(chatHandler); - return this; - } - - /** - * Add the parameter type to this builder's selector. - * - * @param type the type - * @param the return type of the parameter type - * @return this - */ - public CommandBuilder addParameterType(ParameterType type) { - selector.addType(false, type); - return this; - } - - /** - * Add the parameter type to this builder's selector. - * - * @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. - * @param type the type - * @param the return type of the parameter type - * @param the parameter config type (info object) - * @return this - */ - - public CommandBuilder addParameterType(boolean infolessAlias, ParameterType type) { - selector.addType(infolessAlias, type); - return this; - } - - /** - * Get the dispatcher for the root address. - * The dispatcher should be used to finally register all commands, - * after they are all declared. - * - * @return the dispatcher - */ - public ICommandDispatcher getDispatcher() { - return root; - } - - /** - * Print debugging information about the current addresses and commands in this builder - * A StackTraceElement indicating where this was called from is also included - * - * @return this - */ - public CommandBuilder printDebugInformation() { - String address = cur == root ? "" : cur.getAddress(); - StackTraceElement caller = getCallsite(); - - StringBuilder message = new StringBuilder("### CommandBuilder dump ###"); - message.append("\nCalled from ").append(caller); - message.append("\nPosition: ").append(address); - cur.appendDebugInformation(message, "", new HashSet<>()); - - System.out.println(message); - return this; - } - - private static StackTraceElement getCallsite() { - // [0] Thread.currentThread() - // [1] CommandBuilder.getCallsite() - // [2] Calling method - // [3] Method calling the calling method - StackTraceElement[] trace = Thread.currentThread().getStackTrace(); - return trace.length > 3 ? trace[3] : null; - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.chat.IChatHandler; +import io.dico.dicore.command.parameter.type.IParameterTypeSelector; +import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; +import io.dico.dicore.command.parameter.type.ParameterType; +import io.dico.dicore.command.predef.HelpCommand; +import io.dico.dicore.command.predef.PredefinedCommand; +import io.dico.dicore.command.predef.SyntaxCommand; +import io.dico.dicore.command.registration.reflect.ReflectiveRegistration; + +import java.util.HashSet; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Mimic of WorldEdit's CommandGraph + */ +public final class CommandBuilder { + private final RootCommandAddress root; + private ModifiableCommandAddress cur; + private IParameterTypeSelector selector; + + /** + * Instantiate a new CommandBuilder with a new command root system + * Commands registered to this command builder might interfere with + * commands registered to other commands builders or by other plugins. + */ + public CommandBuilder() { + this(new RootCommandAddress()); + } + + /** + * Instantiate a new CommandBuilder with a specified root address. + * If the root address is identical to that of another command builder, + * they will modify the same tree. + * + * @param root the root address + */ + public CommandBuilder(RootCommandAddress root) { + this.root = Objects.requireNonNull(root); + this.cur = root; + this.selector = new MapBasedParameterTypeSelector(true); + } + + /** + * Add a sub command at the current address + * The current address can be inspected using {@link #getAddress()} + * + * @param name the name of the command + * @param command the command executor + * @param aliases any aliases + * @return this + */ + public CommandBuilder addSubCommand(String name, Command command, String... aliases) { + ChildCommandAddress address = new ChildCommandAddress(command); + address.addNameAndAliases(name, aliases); + return addSubCommand(address); + } + + /** + * Add a subcommand as an address at the current address + * The result of this call is the same as + * {@code addSubCommand(address.getMainKey(), address.getCommand(), address.getNames().sublist(1).toArray(new String[0]))} + * + * @param address the address + * @return this + * @throws IllegalArgumentException if {@code address.isRoot()} + */ + public CommandBuilder addSubCommand(ICommandAddress address) { + cur.addChild(address); + return this; + } + + /** + * Search the given class for any (static) methods using command annotations + * 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. + * + * @param clazz the clazz + * @return this + * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class + * @see #registerCommands(Class, Object) + */ + public CommandBuilder registerCommands(Class clazz) { + return registerCommands(clazz, null); + } + + /** + * 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. + * 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. + * + * @param object the object + * @return this + * @throws IllegalArgumentException if an exception occurs while parsing the methods of this class + * @see #registerCommands(Class, Object) + */ + public CommandBuilder registerCommands(Object object) { + return registerCommands(object.getClass(), object); + } + + /** + * Search the given class for methods using command annotations. + * 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. + * The instance is used to invoke non-static methods. + * + * @param clazz the class + * @param instance the instance, null if only static methods + * @return this + * @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 + */ + public CommandBuilder registerCommands(Class clazz, Object instance) { + try { + ReflectiveRegistration.parseCommandGroup(cur, selector, clazz, instance); + return this; + } catch (Exception ex) { + throw new IllegalArgumentException(ex); + } + } + + /** + * register the {@link HelpCommand} as a sub command at the current address + * + * @return this + */ + public CommandBuilder registerHelpCommand() { + HelpCommand.registerAsChild(cur); + return this; + } + + /** + * register the {@link SyntaxCommand} as a sub command a the current address + * + * @return this + */ + public CommandBuilder registerSyntaxCommand() { + SyntaxCommand.registerAsChild(cur); + return this; + } + + /** + * Generate the predefined commands. + * These are presets. + * Examples include {@code help} and {@code syntax}. + *

+ * Predefined commands can be registered through {@link PredefinedCommand#registerPredefinedCommandGenerator(String, Consumer)} + * + * @param commands the commands + * @return this + */ + public CommandBuilder generatePredefinedCommands(String... commands) { + ReflectiveRegistration.generateCommands(cur, commands); + return this; + } + + /** + * Unregister any childs present at the given keys. + *

+ * 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 + * registered by {@link #registerCommands(Class, Object)}, or because you didn't + * 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 keys a varargs array containing the keys + * @return this + * @throws IllegalArgumentException if keys array is empty + */ + public CommandBuilder unregisterCommands(boolean removeAliases, String... keys) { + cur.removeChildren(removeAliases, keys); + return this; + } + + /** + * Jump to the sub-address with the given name as main key. + * If an address with the exact name as main key exists, + * that address becomes the current address. + *

+ * Otherwise, a new addresses is registered with the name and aliases. + * New addresses registered by this command have a HelpCommand added by default. + *

+ * 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()} + * should be made. + *

+ * If the address is the target of a command, it will provide information about its sub commands + * using the HelpCommand. + * + * @param name the main key + * @param aliases the aliases + * @return this + */ + public CommandBuilder group(String name, String... aliases) { + ChildCommandAddress address = cur.getChild(name); + if (address == null || !name.equals(address.getMainKey())) { + address = new ChildCommandAddress(); + address.setupAsPlaceholder(name, aliases); + cur.addChild(address); + } + cur = address; + return this; + } + + /** + * 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}. + * + *

The address must not have a parent or any keys

+ * + * @param address the address object to use + * @param name the main key + * @param aliases any aliases + * @return this + * @throws IllegalArgumentException if any of the requirements set out above aren't met + */ + public CommandBuilder group(ICommandAddress address, String name, String... aliases) { + if (address.hasParent() || address.getMainKey() != null || !(address instanceof ChildCommandAddress)) { + throw new IllegalArgumentException(); + } + + ChildCommandAddress asChild = (ChildCommandAddress) address; + asChild.setupAsPlaceholder(name, aliases); + cur.addChild(address); + cur = asChild; + return this; + } + + /** + * 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...)} + * + * @param shortDescription a short description + * @param description the lines of a full description. + * @return this + * @throws IllegalStateException if the current group has no command + */ + public CommandBuilder setGroupDescription(String shortDescription, String... description) { + Command command = cur.getCommand(); + if (command == null) throw new IllegalStateException(); + cur.setCommand(command + .setShortDescription(shortDescription) + .setDescription(description)); + return this; + } + + /** + * Add a context filter to the command of the current group + * @return this + * @throws IllegalStateException if the current group has no command + */ + public CommandBuilder addContextFilter(IContextFilter contextFilter) { + Command command = cur.getCommand(); + if (command == null) throw new IllegalStateException(); + cur.setCommand(command + .addContextFilter(contextFilter)); + return this; + } + + /** + * Add a required permission to the command of the current group + * @return this + * @throws IllegalStateException if the current group has no command + */ + public CommandBuilder addPermission(String permission) { + return addContextFilter(IContextFilter.permission(permission)); + } + + /** + * Add a required permission to the command of the current group, which can be inherited + * @return this + * @throws IllegalStateException if the current group has no command + */ + public CommandBuilder addInheritablePermission(String permission) { + return addContextFilter(IContextFilter.inheritablePermission(permission)); + } + + /** + * Jump up a level in the address + * + * @return this + * @throws IllegalStateException if the address is empty + * // has a depth of 0 // is at level 0 + */ + public CommandBuilder parent() { + if (cur.hasParent()) { + cur = cur.getParent(); + return this; + } + throw new IllegalStateException("No parent exists at this address"); + } + + /** + * Jump to the root (empty) address, + * such that a subsequent call to {@link #parent()} + * will throw a {@link IllegalStateException} + * + * @return this + */ + public CommandBuilder root() { + cur = root; + return this; + } + + /** + * Get the current address, as a space-separated string + * + * @return the current address + */ + public String getAddress() { + return cur.getAddress(); + } + + /** + * Get the depth of the current address. + * This is equivalent to {@code getAddress().split(" ").length}. + * If the address is empty, the depth is 0. + * + * @return the depth + */ + public int getDepth() { + return cur.getDepth(); + } + + /** + * 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...)} + *

+ * If a call to {@link #setGroupDescription(String, String...)} was made at the same address before, + * the description is copied to the given executor. + * + * @param command the executor + * @return this + * @throws IllegalArgumentException if the command at the address is present and declared by the user, + * in other words, it's not a {@link PredefinedCommand} + */ + public CommandBuilder setCommand(Command command) { + Command current = cur.getCommand(); + if (current instanceof HelpCommand && current != HelpCommand.INSTANCE) { + command.setShortDescription(current.getShortDescription()); + command.setDescription(current.getDescription()); + } + + cur.setCommand(command); + return this; + } + + /** + * Configure the chat handler at this address. The chat handler + * is used for all children down the tree if they don't explicitly have + * their own chat handler configured. If this isn't configured, + * {@code ChatHandlers.defaultChat()} is used. + * + * @param chatHandler the chat handler + * @return this + */ + public CommandBuilder setChatHandler(IChatHandler chatHandler) { + cur.setChatHandler(chatHandler); + return this; + } + + /** + * Add the parameter type to this builder's selector. + * + * @param type the type + * @param the return type of the parameter type + * @return this + */ + public CommandBuilder addParameterType(ParameterType type) { + selector.addType(false, type); + return this; + } + + /** + * Add the parameter type to this builder's selector. + * + * @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. + * @param type the type + * @param the return type of the parameter type + * @param the parameter config type (info object) + * @return this + */ + + public CommandBuilder addParameterType(boolean infolessAlias, ParameterType type) { + selector.addType(infolessAlias, type); + return this; + } + + /** + * Get the dispatcher for the root address. + * The dispatcher should be used to finally register all commands, + * after they are all declared. + * + * @return the dispatcher + */ + public ICommandDispatcher getDispatcher() { + return root; + } + + /** + * Print debugging information about the current addresses and commands in this builder + * A StackTraceElement indicating where this was called from is also included + * + * @return this + */ + public CommandBuilder printDebugInformation() { + String address = cur == root ? "" : cur.getAddress(); + StackTraceElement caller = getCallsite(); + + StringBuilder message = new StringBuilder("### CommandBuilder dump ###"); + message.append("\nCalled from ").append(caller); + message.append("\nPosition: ").append(address); + cur.appendDebugInformation(message, "", new HashSet<>()); + + System.out.println(message); + return this; + } + + private static StackTraceElement getCallsite() { + // [0] Thread.currentThread() + // [1] CommandBuilder.getCallsite() + // [2] Calling method + // [3] Method calling the calling method + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + return trace.length > 3 ? trace[3] : null; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java index b859952..adf66de 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/CommandException.java @@ -1,28 +1,28 @@ -package io.dico.dicore.command; - -public class CommandException extends Exception { - - public CommandException() { - } - - public CommandException(String message) { - super(message); - } - - public CommandException(String message, Throwable cause) { - super(message, cause); - } - - public CommandException(Throwable cause) { - super(cause); - } - - public static CommandException missingArgument(String parameterName) { - return new CommandException("Missing argument for " + parameterName); - } - - public static CommandException invalidArgument(String parameterName, String syntaxHelp) { - return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp); - } - -} +package io.dico.dicore.command; + +public class CommandException extends Exception { + + public CommandException() { + } + + public CommandException(String message) { + super(message); + } + + public CommandException(String message, Throwable cause) { + super(message, cause); + } + + public CommandException(Throwable cause) { + super(cause); + } + + public static CommandException missingArgument(String parameterName) { + return new CommandException("Missing argument for " + parameterName); + } + + public static CommandException invalidArgument(String parameterName, String syntaxHelp) { + return new CommandException("Invalid input for " + parameterName + ", should be " + syntaxHelp); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java index fba8780..0b02459 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/EMessageType.java @@ -1,19 +1,19 @@ -package io.dico.dicore.command; - -public enum EMessageType { - GOOD_NEWS, - BAD_NEWS, - NEUTRAL, - INFORMATIVE, - WARNING, - INSTRUCTION, - EXCEPTION, - RESULT, - CUSTOM, - - DESCRIPTION, - SYNTAX, - HIGHLIGHT, - SUBCOMMAND, - NUMBER, -} +package io.dico.dicore.command; + +public enum EMessageType { + GOOD_NEWS, + BAD_NEWS, + NEUTRAL, + INFORMATIVE, + WARNING, + INSTRUCTION, + EXCEPTION, + RESULT, + CUSTOM, + + DESCRIPTION, + SYNTAX, + HIGHLIGHT, + SUBCOMMAND, + NUMBER, +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java index 83b0151..980477a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/EOverridePolicy.java @@ -1,12 +1,12 @@ -package io.dico.dicore.command; - -/** - * Override policies for registering to the command map - */ -public enum EOverridePolicy { - OVERRIDE_ALL, - MAIN_KEY_ONLY, - MAIN_AND_FALLBACK, - FALLBACK_ONLY, - OVERRIDE_NONE -} +package io.dico.dicore.command; + +/** + * Override policies for registering to the command map + */ +public enum EOverridePolicy { + OVERRIDE_ALL, + MAIN_KEY_ONLY, + MAIN_AND_FALLBACK, + FALLBACK_ONLY, + OVERRIDE_NONE +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java index a329f40..af42650 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExecutionContext.java @@ -1,385 +1,385 @@ -package io.dico.dicore.command; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.ContextParser; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.*; - -/** - * The context of execution. - *

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

+ * 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. + */ +public class ExecutionContext { + // Sender of the command + private final CommandSender sender; + // Address while parsing parameters with ContextParser + private ICommandAddress address; + // Command to execute + private Command command; + // if this flag is set, this execution is only for completion purposes. + private boolean tabComplete; + + private final ArgumentBuffer buffer; + // private ArgumentBuffer processedBuffer; + + // caches the buffer's cursor before parsing. This is needed to provide the original input of the player. + private int cursorStart; + + // when the context starts parsing parameters, this flag is set, and any subsequent calls to #parseParameters() throw an IllegalStateException. + //private boolean attemptedToParse; + + + // 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. + private Map parameterValueMap = new HashMap<>(); + // this set contains the names of the parameters that were present in the command, and not given a default value. + private Set parsedParameters = new HashSet<>(); + + + // these fields store information required to provide completions. + // 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. + private Parameter parameterToComplete; + // this is the cursor that the ArgumentBuffer is reset to when suggested completions are requested. + private int parameterToCompleteCursor = -1; + + // if this flag is set, any messages sent through the sendMessage methods are discarded. + private boolean muted; + + public ExecutionContext(CommandSender sender, ArgumentBuffer buffer, boolean tabComplete) { + this.sender = Objects.requireNonNull(sender); + this.buffer = Objects.requireNonNull(buffer); + this.muted = tabComplete; + this.tabComplete = tabComplete; + + // 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. + // This allows the parser to correctly identify the parameter to be completed in this case. + if (!tabComplete) { + buffer.dropTrailingEmptyElements(); + } + } + + /** + * Construct an execution context that is ready to parse the parameter values. + * + * @param sender the sender + * @param address the address + * @param command the command + * @param buffer the arguments + * @param tabComplete true if this execution is a tab-completion + */ + public ExecutionContext(CommandSender sender, ICommandAddress address, Command command, ArgumentBuffer buffer, boolean tabComplete) { + this(sender, buffer, tabComplete); + setAddress(address); + setCommand(command); + } + + /** + * Sender of the command + * + * @return the sender of the command + */ + public CommandSender getSender() { + return sender; + } + + /** + * @return the buffer of arguments + */ + public ArgumentBuffer getBuffer() { + return buffer; + } + + /** + * Command's address + * + * @return the command's address + */ + public ICommandAddress getAddress() { + return address; + } + + /** + * Set the address + * + * @param address the new address + */ + public void setAddress(ICommandAddress address) { + this.address = address; + } + + /** + * The command + * + * @return the command + */ + public Command getCommand() { + return command; + } + + /** + * Set the command + * + * @param command the new command + */ + public void setCommand(Command command) { + this.command = command; + } + + /** + * @return true if this context is for a tab completion. + */ + public boolean isTabComplete() { + return tabComplete; + } + + /** + * @return true if this context is muted. + */ + public boolean isMuted() { + return muted; + } + + /** + * Parse parameters from the given parameter list, + * adding their values to the cache of this context. + * + * @param parameterList the parameterList + * @throws CommandException if the arguments are not valid + */ + public void parse(ParameterList parameterList) throws CommandException { + cursorStart = buffer.getCursor(); + + ContextParser parser = new ContextParser(this, parameterList, parameterValueMap, parsedParameters); + + try { + parser.parse(); + } finally { + if (tabComplete) { + parameterToComplete = parser.getCompletionTarget(); + parameterToCompleteCursor = parser.getCompletionCursor(); + } + } + + } + + /** + * The command's parameter definition. + * + * @return the parameter list + */ + @Deprecated + public ParameterList getParameterList() { + return null;//command.getParameterList(); + } + + /** + * Get the buffer as it was before preprocessing the arguments. + * + * @return the original buffer + */ + @Deprecated + public ArgumentBuffer getOriginalBuffer() { + return buffer; + } + + /** + * The arguments + * + * @return the argument buffer + */ + @Deprecated + public ArgumentBuffer getProcessedBuffer() { + return buffer; + } + + /** + * The cursor start, in other words, the buffer's cursor before parameters were parsed. + * + * @return the cursor start + */ + public int getCursorStart() { + return cursorStart; + } + + /** + * The original arguments. + * + * @return original arguments. + */ + public String[] getOriginal() { + return buffer.getArrayFromIndex(cursorStart); + } + + /** + * The path used to access this address. + * + * @return the path used to access this address. + */ + public String[] getRoute() { + return Arrays.copyOf(buffer.toArray(), address.getDepth()); + } + + public Formatting getFormat(EMessageType type) { + return address.getChatHandler().getChatFormatForType(type); + } + + /** + * The full command as cached by the buffer. Might be incomplete depending on how it was dispatched. + * + * @return the full command + */ + public String getRawInput() { + return buffer.getRawInput(); + } + + /** + * Get the value of the parameter with the given name + * + * @param name the parameter's name + * @param expected type + * @return the parsed value or the default value + */ + @SuppressWarnings("unchecked") + public T get(String name) { + if (!parameterValueMap.containsKey(name)) { + throw new IllegalArgumentException(); + } + + try { + return (T) parameterValueMap.get(name); + } catch (ClassCastException ex) { + throw new IllegalArgumentException("Invalid type parameter requested for parameter " + name, ex); + } + } + + /** + * Get the value of the flag with the given name + * + * @param flag the flag's name, without preceding "-" + * @param expected type + * @return the parsed value or the default value + */ + public T getFlag(String flag) { + return get("-" + flag); + } + + @SuppressWarnings("unchecked") + @Deprecated + public T get(int index) { + return null;//get(getParameterList().getIndexedParameterName(index)); + } + + /** + * Checks if the parameter by the name was provided in the command's arguments. + * + * @param name the parameter name + * @return true if it was provided + */ + public boolean isProvided(String name) { + return parsedParameters.contains(name); + } + + /** + * Checks if the parameter by the index was provided in the command's arguments. + * + * @param index the parameter index + * @return true if it was provided + */ + @Deprecated + public boolean isProvided(int index) { + return false;//isProvided(getParameterList().getIndexedParameterName(index)); + } + + /** + * The parameter to complete. + * This parameter is requested suggestions + * + * @return the parameter to complete. + */ + public Parameter getParameterToComplete() { + return parameterToComplete; + } + + /** + * 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. + * @return completions. + */ + public List getSuggestedCompletions(Location location) { + if (parameterToComplete != null) { + return parameterToComplete.complete(this, location, buffer.getUnaffectingCopy().setCursor(parameterToCompleteCursor)); + } + + List result = new ArrayList<>(); + for (String name : parameterValueMap.keySet()) { + if (name.startsWith("-") && !parsedParameters.contains(name)) { + result.add(name); + } + } + return result; + } + + /* + Chat handling + */ + + public void sendMessage(String message) { + sendMessage(true, message); + } + + public void sendMessage(EMessageType messageType, String message) { + sendMessage(messageType, true, message); + } + + public void sendMessage(boolean translateColours, String message) { + sendMessage(EMessageType.NEUTRAL, translateColours, message); + } + + public void sendMessage(EMessageType messageType, boolean translateColours, String message) { + if (!muted) { + if (translateColours) { + message = Formatting.translateChars('&', message); + } + address.getChatHandler().sendMessage(this, messageType, message); + } + } + + public void sendMessage(String messageFormat, Object... args) { + sendMessage(true, messageFormat, args); + } + + public void sendMessage(EMessageType messageType, String messageFormat, Object... args) { + sendMessage(messageType, true, messageFormat, args); + } + + public void sendMessage(boolean translateColours, String messageFormat, Object... args) { + sendMessage(EMessageType.NEUTRAL, translateColours, messageFormat, args); + } + + public void sendMessage(EMessageType messageType, boolean translateColours, String messageFormat, Object... args) { + sendMessage(messageType, translateColours, String.format(messageFormat, args)); + } + + public void sendHelpMessage(int page) { + if (!muted) { + address.getChatHandler().sendHelpMessage(sender, this, address, page); + } + } + + public void sendSyntaxMessage() { + if (!muted) { + address.getChatHandler().sendSyntaxMessage(sender, this, address); + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java index 47c2aca..eb93b30 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ExtendedCommand.java @@ -1,64 +1,64 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.parameter.IArgumentPreProcessor; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.type.ParameterType; - -@SuppressWarnings("unchecked") -public abstract class ExtendedCommand> extends Command { - protected boolean modifiable; - - public ExtendedCommand() { - this(true); - } - - public ExtendedCommand(boolean modifiable) { - this.modifiable = modifiable; - } - - protected T newModifiableInstance() { - return (T) this; - } - - @Override - public T addParameter(Parameter parameter) { - return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter); - } - - @Override - public T addContextFilter(IContextFilter contextFilter) { - return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter); - } - - @Override - public T removeContextFilter(IContextFilter contextFilter) { - return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter); - } - - @Override - public T requiredParameters(int requiredParameters) { - return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters); - } - - @Override - public T repeatFinalParameter() { - return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter(); - } - - @Override - public T setDescription(String... description) { - return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description); - } - - @Override - public T setShortDescription(String shortDescription) { - return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription); - } - - /* - @Override - public T preprocessArguments(IArgumentPreProcessor processor) { - return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor); - }*/ - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.parameter.IArgumentPreProcessor; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.type.ParameterType; + +@SuppressWarnings("unchecked") +public abstract class ExtendedCommand> extends Command { + protected boolean modifiable; + + public ExtendedCommand() { + this(true); + } + + public ExtendedCommand(boolean modifiable) { + this.modifiable = modifiable; + } + + protected T newModifiableInstance() { + return (T) this; + } + + @Override + public T addParameter(Parameter parameter) { + return modifiable ? (T) super.addParameter(parameter) : newModifiableInstance().addParameter(parameter); + } + + @Override + public T addContextFilter(IContextFilter contextFilter) { + return modifiable ? (T) super.addContextFilter(contextFilter) : newModifiableInstance().addContextFilter(contextFilter); + } + + @Override + public T removeContextFilter(IContextFilter contextFilter) { + return modifiable ? (T) super.removeContextFilter(contextFilter) : newModifiableInstance().removeContextFilter(contextFilter); + } + + @Override + public T requiredParameters(int requiredParameters) { + return modifiable ? (T) super.requiredParameters(requiredParameters) : newModifiableInstance().requiredParameters(requiredParameters); + } + + @Override + public T repeatFinalParameter() { + return modifiable ? (T) super.repeatFinalParameter() : newModifiableInstance().repeatFinalParameter(); + } + + @Override + public T setDescription(String... description) { + return modifiable ? (T) super.setDescription(description) : newModifiableInstance().setDescription(description); + } + + @Override + public T setShortDescription(String shortDescription) { + return modifiable ? (T) super.setShortDescription(shortDescription) : newModifiableInstance().setShortDescription(shortDescription); + } + + /* + @Override + public T preprocessArguments(IArgumentPreProcessor processor) { + return modifiable ? (T) super.preprocessArguments(processor) : newModifiableInstance().preprocessArguments(processor); + }*/ + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java index 158b1f0..f900139 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandAddress.java @@ -1,205 +1,205 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.ParameterList; -import io.dico.dicore.command.predef.PredefinedCommand; -import org.bukkit.command.CommandSender; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Interface for an address of a command. - *

- * The address holds what the name and aliases of a command are. - * The address also (optionally) holds a reference to a {@link Command} - *

- * One instance of {@link Command} can be held by multiple addresses, - * because the address decides what the command's name and aliases are. - *

- * 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 - * and no children deeper down match the command (there are exceptions to the later as defined by - * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - * and {@link Command#isVisibleTo(CommandSender)} - */ -public interface ICommandAddress { - - /** - * @return true if this address has a parent. - */ - boolean hasParent(); - - /** - * Get the parent of this address - * - * @return the parent of this address, or null if none exists. - */ - ICommandAddress getParent(); - - /** - * @return true if this address has a command. - */ - boolean hasCommand(); - - /** - * @return true if this address has a command that is not an instance of {@link PredefinedCommand} - */ - boolean hasUserDeclaredCommand(); - - /** - * @return Get the command of this address, or null if none exists. - */ - Command getCommand(); - - /** - * @return true if this address is an instance of {@link RootCommandAddress} - */ - boolean isRoot(); - - /** - * @return the root address of the tree which this address resides in. - */ - ICommandAddress getRoot(); - - /** - * A list of the names of this address, at the current level. - * The first entry is the main key, the subsequent ones are aliases. - *

- * Untill an address is assigned a parent, this list is mutable. - *

- * If {@link #isRoot()}, this returns an immutable, empty list. - * - * @return the list of names. - */ - List getNames(); - - /** - * A list of the aliases of this address. That is, {@link #getNames()} - * without the first entry. - * - * @return a list of aliases - */ - List getAliases(); - - /** - * @return The first element of {@link #getNames()} - */ - String getMainKey(); - - /** - * 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. - * In other words, the command without the / that is required to target the command at this address. - * - * @return the address of this command. - */ - String getAddress(); - - /** - * 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. - * - * @return The depth of this address - */ - int getDepth(); - - /** - * @return true if the depth of this address is larger than the argument. - */ - boolean isDepthLargerThan(int depth); - - /** - * @return true if this address has any children. - */ - boolean hasChildren(); - - /** - * @return total number of children, not considering any aliases - */ - int getNumberOfRealChildren(); - - /** - * Get an unmodifiable view of all main keys of the children of this address. - * - * @return the main keys - */ - Collection getChildrenMainKeys(); - - /** - * Get an unmodifiable view of the children of this address. - * Values might be duplicated for aliases. - * - *

- * To iterate children without duplicates, you can do something like this: - *

{@code
-     *     for (String key : address.getChildrenMainKeys()) {
-     *         ICommandAddress child = address.getChild(key);
-     *         // do stuff with child
-     *     }
-     *     }
- *

- * - * @return the children of this address. - */ - Map getChildren(); - - /** - * Query for a child at the given key. - * - * @param key the key. The name or alias of a command. - * @return the child, or null if it's not found - */ - ICommandAddress getChild(String key); - - /** - * Query for a child using the given buffer, with the given context for reference. - * Can be used to override behaviour of the address tree. - *

- * The default implementation is as follows: - *

{@code
-     * return buffer.hasNext() ? getChild(buffer.next()) : null;
-     * }
- * - * @param context context of a command being executed - * @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 - */ - ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; - - /** - * Get the command dispatcher for this tree - * - * @return the command dispatcher - */ - ICommandDispatcher getDispatcherForTree(); - - /** - * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler. - */ - IChatHandler getChatHandler(); - - /** - * 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. - * Its parameters are parsed and added to the context. - * - * @return true if the command attached to this address should be treated as trailing. - */ - boolean isCommandTrailing(); - - static ICommandAddress newChild() { - return new ChildCommandAddress(); - } - - static ICommandAddress newChild(Command command) { - return new ChildCommandAddress(command); - } - - static ICommandAddress newRoot() { - return new RootCommandAddress(); - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.chat.IChatHandler; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.ParameterList; +import io.dico.dicore.command.predef.PredefinedCommand; +import org.bukkit.command.CommandSender; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Interface for an address of a command. + *

+ * The address holds what the name and aliases of a command are. + * The address also (optionally) holds a reference to a {@link Command} + *

+ * One instance of {@link Command} can be held by multiple addresses, + * because the address decides what the command's name and aliases are. + *

+ * 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 + * and no children deeper down match the command (there are exceptions to the later as defined by + * {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} + * and {@link Command#isVisibleTo(CommandSender)} + */ +public interface ICommandAddress { + + /** + * @return true if this address has a parent. + */ + boolean hasParent(); + + /** + * Get the parent of this address + * + * @return the parent of this address, or null if none exists. + */ + ICommandAddress getParent(); + + /** + * @return true if this address has a command. + */ + boolean hasCommand(); + + /** + * @return true if this address has a command that is not an instance of {@link PredefinedCommand} + */ + boolean hasUserDeclaredCommand(); + + /** + * @return Get the command of this address, or null if none exists. + */ + Command getCommand(); + + /** + * @return true if this address is an instance of {@link RootCommandAddress} + */ + boolean isRoot(); + + /** + * @return the root address of the tree which this address resides in. + */ + ICommandAddress getRoot(); + + /** + * A list of the names of this address, at the current level. + * The first entry is the main key, the subsequent ones are aliases. + *

+ * Untill an address is assigned a parent, this list is mutable. + *

+ * If {@link #isRoot()}, this returns an immutable, empty list. + * + * @return the list of names. + */ + List getNames(); + + /** + * A list of the aliases of this address. That is, {@link #getNames()} + * without the first entry. + * + * @return a list of aliases + */ + List getAliases(); + + /** + * @return The first element of {@link #getNames()} + */ + String getMainKey(); + + /** + * 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. + * In other words, the command without the / that is required to target the command at this address. + * + * @return the address of this command. + */ + String getAddress(); + + /** + * 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. + * + * @return The depth of this address + */ + int getDepth(); + + /** + * @return true if the depth of this address is larger than the argument. + */ + boolean isDepthLargerThan(int depth); + + /** + * @return true if this address has any children. + */ + boolean hasChildren(); + + /** + * @return total number of children, not considering any aliases + */ + int getNumberOfRealChildren(); + + /** + * Get an unmodifiable view of all main keys of the children of this address. + * + * @return the main keys + */ + Collection getChildrenMainKeys(); + + /** + * Get an unmodifiable view of the children of this address. + * Values might be duplicated for aliases. + * + *

+ * To iterate children without duplicates, you can do something like this: + *

{@code
+     *     for (String key : address.getChildrenMainKeys()) {
+     *         ICommandAddress child = address.getChild(key);
+     *         // do stuff with child
+     *     }
+     *     }
+ *

+ * + * @return the children of this address. + */ + Map getChildren(); + + /** + * Query for a child at the given key. + * + * @param key the key. The name or alias of a command. + * @return the child, or null if it's not found + */ + ICommandAddress getChild(String key); + + /** + * Query for a child using the given buffer, with the given context for reference. + * Can be used to override behaviour of the address tree. + *

+ * The default implementation is as follows: + *

{@code
+     * return buffer.hasNext() ? getChild(buffer.next()) : null;
+     * }
+ * + * @param context context of a command being executed + * @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 + */ + ICommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; + + /** + * Get the command dispatcher for this tree + * + * @return the command dispatcher + */ + ICommandDispatcher getDispatcherForTree(); + + /** + * @return The desired chathandler for use by commands at this address and any sub-addresses, if they define no explicit chat handler. + */ + IChatHandler getChatHandler(); + + /** + * 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. + * Its parameters are parsed and added to the context. + * + * @return true if the command attached to this address should be treated as trailing. + */ + boolean isCommandTrailing(); + + static ICommandAddress newChild() { + return new ChildCommandAddress(); + } + + static ICommandAddress newChild(Command command) { + return new ChildCommandAddress(command); + } + + static ICommandAddress newRoot() { + return new RootCommandAddress(); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java index 055171d..4f3ffc6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ICommandDispatcher.java @@ -1,146 +1,146 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.registration.CommandMap; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.List; -import java.util.Map; - -public interface ICommandDispatcher { - - /** - * 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. - * @return the child, or this same instance of no child is found. - */ - ICommandAddress getDeepChild(ArgumentBuffer buffer); - - /** - * Similar to {@link #getDeepChild(ArgumentBuffer)}, - * but this method incorporates checks on the command of traversed children: - * {@link Command#isVisibleTo(CommandSender)} - * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - *

- * 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 buffer the command itself as a buffer. - * @return the address that is the target of the command. - */ - @Deprecated - ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer); - - /** - * Similar to {@link #getDeepChild(ArgumentBuffer)}, - * but this method incorporates checks on the command of traversed children: - * {@link Command#isVisibleTo(CommandSender)} - * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} - *

- * 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 buffer the command itself as a buffer. - * @return the address that is the target of the command. - */ - ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; - - /** - * dispatch the command - * - * @param sender the sender - * @param command the command - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, String[] command); - - /** - * dispatch the command - * - * @param sender the sender - * @param usedLabel the label (word after the /) - * @param args the arguments - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args); - - /** - * dispatch the command - * - * @param sender the sender - * @param buffer the command - * @return true if a command has executed - */ - boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer); - - /** - * suggest tab completions - * - * @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 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. - * @return tab completions - */ - List getTabCompletions(CommandSender sender, Location location, String[] args); - - /** - * suggest tab completions - * - * @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 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)} - * @return tab completions - */ - List getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args); - - /** - * suggest tab completions - * - * @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 buffer the arguments as a buffer - * @return tab completions - */ - List getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer); - - /** - * Register this dispatcher's commands to the command map - * - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - default void registerToCommandMap() { - registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL); - } - - /** - * Register this dispatcher's commands to the command map - * - * @param fallbackPrefix the fallback prefix to use, null if none - * @param overridePolicy the override policy - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) { - registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy); - } - - /** - * Register this dispatcher's commands to the command map - * - * @param fallbackPrefix the fallback prefix to use, null if none - * @param map the command map - * @param overridePolicy the override policy - * @throws UnsupportedOperationException if this dispatcher is not the root of the tree - */ - void registerToCommandMap(String fallbackPrefix, Map map, EOverridePolicy overridePolicy); - - default void unregisterFromCommandMap() { - unregisterFromCommandMap(CommandMap.getCommandMap()); - } - - void unregisterFromCommandMap(Map map); - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.registration.CommandMap; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.Map; + +public interface ICommandDispatcher { + + /** + * 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. + * @return the child, or this same instance of no child is found. + */ + ICommandAddress getDeepChild(ArgumentBuffer buffer); + + /** + * Similar to {@link #getDeepChild(ArgumentBuffer)}, + * but this method incorporates checks on the command of traversed children: + * {@link Command#isVisibleTo(CommandSender)} + * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} + *

+ * 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 buffer the command itself as a buffer. + * @return the address that is the target of the command. + */ + @Deprecated + ICommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer); + + /** + * Similar to {@link #getDeepChild(ArgumentBuffer)}, + * but this method incorporates checks on the command of traversed children: + * {@link Command#isVisibleTo(CommandSender)} + * and {@link Command#takePrecedenceOverSubcommand(String, ArgumentBuffer)} + *

+ * 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 buffer the command itself as a buffer. + * @return the address that is the target of the command. + */ + ICommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException; + + /** + * dispatch the command + * + * @param sender the sender + * @param command the command + * @return true if a command has executed + */ + boolean dispatchCommand(CommandSender sender, String[] command); + + /** + * dispatch the command + * + * @param sender the sender + * @param usedLabel the label (word after the /) + * @param args the arguments + * @return true if a command has executed + */ + boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args); + + /** + * dispatch the command + * + * @param sender the sender + * @param buffer the command + * @return true if a command has executed + */ + boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer); + + /** + * suggest tab completions + * + * @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 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. + * @return tab completions + */ + List getTabCompletions(CommandSender sender, Location location, String[] args); + + /** + * suggest tab completions + * + * @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 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)} + * @return tab completions + */ + List getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args); + + /** + * suggest tab completions + * + * @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 buffer the arguments as a buffer + * @return tab completions + */ + List getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer); + + /** + * Register this dispatcher's commands to the command map + * + * @throws UnsupportedOperationException if this dispatcher is not the root of the tree + */ + default void registerToCommandMap() { + registerToCommandMap(null, CommandMap.getCommandMap(), EOverridePolicy.OVERRIDE_ALL); + } + + /** + * Register this dispatcher's commands to the command map + * + * @param fallbackPrefix the fallback prefix to use, null if none + * @param overridePolicy the override policy + * @throws UnsupportedOperationException if this dispatcher is not the root of the tree + */ + default void registerToCommandMap(String fallbackPrefix, EOverridePolicy overridePolicy) { + registerToCommandMap(fallbackPrefix, CommandMap.getCommandMap(), overridePolicy); + } + + /** + * Register this dispatcher's commands to the command map + * + * @param fallbackPrefix the fallback prefix to use, null if none + * @param map the command map + * @param overridePolicy the override policy + * @throws UnsupportedOperationException if this dispatcher is not the root of the tree + */ + void registerToCommandMap(String fallbackPrefix, Map map, EOverridePolicy overridePolicy); + + default void unregisterFromCommandMap() { + unregisterFromCommandMap(CommandMap.getCommandMap()); + } + + void unregisterFromCommandMap(Map map); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java index 5c05e27..bf34873 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/IContextFilter.java @@ -1,201 +1,201 @@ -package io.dico.dicore.command; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedConsumer; -import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public interface IContextFilter extends Comparable { - - /** - * Filter the given context by this filter's criteria. - * If the context does not match the criteria, an exception is thrown describing the problem. - * - * @param context the context to match - * @throws CommandException if it doesn't match - */ - void filterContext(ExecutionContext context) throws CommandException; - - /** - * 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 path the path traversed from the command that registered this filter to the executed command - */ - default void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { - filterContext(subContext); - } - - /** - * Get the priority of this context filter. - * The priorities determine the order in which a command's context filters are executed. - * - * @return the priority - */ - Priority getPriority(); - - default boolean allowsContext(ExecutionContext context) { - try { - filterContext(context); - return true; - } catch (CommandException ex) { - return false; - } - } - - /** - * Used to sort filters in execution order. That is, filters are ordered by {@link #getPriority()} - * - * @param o compared filter - * @return comparison value - */ - @Override - default int compareTo(@NotNull IContextFilter o) { - return getPriority().compareTo(o.getPriority()); - } - - /* - default boolean isInheritable() { - return false; - } - - default IContextFilter inherit(String... components) { - if (!isInheritable()) { - throw new IllegalStateException("This IContextFilter cannot be inherited"); - } - - return this; - }*/ - - /** - * IContextFilter priorities. Executes from top to bottom. - */ - enum Priority { - /** - * This priority should have checks on the sender type. - * Any filters on this priority are tested before permissions are. - * This is the highest priority. - */ - VERY_EARLY, // sender type check - - /** - * This priority is specific to permissions. - */ - PERMISSION, - - /** - * Early priority. Post permissions, pre parameter-parsing. - */ - EARLY, - - /** - * Normal priority. Post permissions, pre parameter-parsing. - */ - NORMAL, - - /** - * Late priority. Post permissions, pre parameter-parsing. - */ - LATE, - - /** - * Very late priority. Post permissions, pre parameter-parsing. - */ - VERY_LATE, - - /** - * Post parameters priority. Post permissions, post parameter-parsing. - * This is the lowest priority. - */ - POST_PARAMETERS; - - private IContextFilter inheritor; - - /** - * 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. - * - * @return the inheritor - */ - public IContextFilter getInheritor() { - if (inheritor == null) { - inheritor = InheritingContextFilter.inheritingPriority(this); - } - return inheritor; - } - - } - - /** - * Ensures that only {@link org.bukkit.entity.Player} type senders can execute the command. - */ - IContextFilter PLAYER_ONLY = filterSender(Priority.VERY_EARLY, Validate::isPlayer); - - /** - * Ensures that only {@link org.bukkit.command.ConsoleCommandSender} type senders can execute the command. - */ - IContextFilter CONSOLE_ONLY = filterSender(Priority.VERY_EARLY, Validate::isConsole); - - /** - * This filter is not working as intended. - *

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

+ * 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(); + + static IContextFilter fromCheckedRunnable(Priority priority, CheckedRunnable runnable) { + return new IContextFilter() { + @Override + public void filterContext(ExecutionContext context) throws CommandException { + runnable.checkedRun(); + } + + @Override + public Priority getPriority() { + return priority; + } + }; + } + + static IContextFilter filterSender(Priority priority, CheckedConsumer consumer) { + return new IContextFilter() { + @Override + public void filterContext(ExecutionContext context) throws CommandException { + consumer.checkedAccept(context.getSender()); + } + + @Override + public Priority getPriority() { + return priority; + } + }; + } + + static IContextFilter permission(String permission) { + return new PermissionContextFilter(permission); + } + + static IContextFilter permission(String permission, String failMessage) { + return new PermissionContextFilter(permission, failMessage); + } + + static IContextFilter inheritablePermission(String permission) { + return new PermissionContextFilter(permission, true); + } + + /** + * Produce an inheritable permission context filter. + * 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 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 + * @return the context filter + * @throws IllegalArgumentException if componentInsertionIndex is out of range + */ + static IContextFilter inheritablePermission(String permission, int componentInsertionIndex, String failMessage) { + return new PermissionContextFilter(permission, componentInsertionIndex, failMessage); + } + +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java index 0b4875b..58cd7a6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/InheritingContextFilter.java @@ -1,64 +1,64 @@ -package io.dico.dicore.command; - -import java.util.List; - -public abstract class InheritingContextFilter implements IContextFilter { - private static final String[] emptyStringArray = new String[0]; - - private static String[] addParent(String[] path, String parent) { - String[] out = new String[path.length + 1]; - System.arraycopy(path, 0, out, 1, path.length); - out[0] = parent; - return out; - } - - protected abstract boolean isInherited(IContextFilter filter); - - @Override - public void filterContext(ExecutionContext context) throws CommandException { - ICommandAddress address = context.getAddress(); - - String[] traversedPath = emptyStringArray; - do { - traversedPath = addParent(traversedPath, address.getMainKey()); - address = address.getParent(); - - if (address != null && address.hasCommand()) { - boolean doBreak = true; - - Command command = address.getCommand(); - List contextFilterList = command.getContextFilters(); - for (IContextFilter filter : contextFilterList) { - if (isInherited(filter)) { - if (filter == this) { - // do the same for next parent - // this method is necessary to keep traversedPath information - doBreak = false; - } else { - filter.filterSubContext(context, traversedPath); - } - } - } - - if (doBreak) { - break; - } - } - } while (address != null); - } - - static InheritingContextFilter inheritingPriority(Priority priority) { - return new InheritingContextFilter() { - @Override - protected boolean isInherited(IContextFilter filter) { - return filter.getPriority() == priority; - } - - @Override - public Priority getPriority() { - return priority; - } - }; - } - -} +package io.dico.dicore.command; + +import java.util.List; + +public abstract class InheritingContextFilter implements IContextFilter { + private static final String[] emptyStringArray = new String[0]; + + private static String[] addParent(String[] path, String parent) { + String[] out = new String[path.length + 1]; + System.arraycopy(path, 0, out, 1, path.length); + out[0] = parent; + return out; + } + + protected abstract boolean isInherited(IContextFilter filter); + + @Override + public void filterContext(ExecutionContext context) throws CommandException { + ICommandAddress address = context.getAddress(); + + String[] traversedPath = emptyStringArray; + do { + traversedPath = addParent(traversedPath, address.getMainKey()); + address = address.getParent(); + + if (address != null && address.hasCommand()) { + boolean doBreak = true; + + Command command = address.getCommand(); + List contextFilterList = command.getContextFilters(); + for (IContextFilter filter : contextFilterList) { + if (isInherited(filter)) { + if (filter == this) { + // do the same for next parent + // this method is necessary to keep traversedPath information + doBreak = false; + } else { + filter.filterSubContext(context, traversedPath); + } + } + } + + if (doBreak) { + break; + } + } + } while (address != null); + } + + static InheritingContextFilter inheritingPriority(Priority priority) { + return new InheritingContextFilter() { + @Override + protected boolean isInherited(IContextFilter filter) { + return filter.getPriority() == priority; + } + + @Override + public Priority getPriority() { + return priority; + } + }; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java index 71b5ca4..5f55122 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/LambdaCommand.java @@ -1,35 +1,35 @@ -package io.dico.dicore.command; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.List; -import java.util.Objects; -import java.util.function.BiFunction; - -public class LambdaCommand extends ExtendedCommand { - private CheckedBiFunction executor; - private BiFunction> completer; - - public LambdaCommand executor(CheckedBiFunction executor) { - this.executor = Objects.requireNonNull(executor); - return this; - } - - public LambdaCommand completer(BiFunction> completer) { - this.completer = Objects.requireNonNull(completer); - return this; - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - return executor.checkedApply(sender, context); - } - - @Override - public List tabComplete(CommandSender sender, ExecutionContext context, Location location) { - return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context); - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.exceptions.checkedfunctions.CheckedBiFunction; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.Objects; +import java.util.function.BiFunction; + +public class LambdaCommand extends ExtendedCommand { + private CheckedBiFunction executor; + private BiFunction> completer; + + public LambdaCommand executor(CheckedBiFunction executor) { + this.executor = Objects.requireNonNull(executor); + return this; + } + + public LambdaCommand completer(BiFunction> completer) { + this.completer = Objects.requireNonNull(completer); + return this; + } + + @Override + public String execute(CommandSender sender, ExecutionContext context) throws CommandException { + return executor.checkedApply(sender, context); + } + + @Override + public List tabComplete(CommandSender sender, ExecutionContext context, Location location) { + return completer == null ? super.tabComplete(sender, context, location) : completer.apply(sender, context); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java index 0cfd755..70e3e03 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/ModifiableCommandAddress.java @@ -1,312 +1,312 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.chat.ChatHandlers; -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.ParameterList; -import io.dico.dicore.command.predef.DefaultGroupCommand; -import io.dico.dicore.command.predef.HelpCommand; -import io.dico.dicore.command.predef.PredefinedCommand; - -import java.util.*; - -public abstract class ModifiableCommandAddress implements ICommandAddress { - Map children; - Collection childrenMainKeys = Collections.emptyList(); - - // the chat handler as configured by the programmer - IChatHandler chatHandler; - // cache for the algorithm that finds the first chat handler going up the tree - transient IChatHandler cachedChatHandlerFallback; - - ModifiableCommandAddress helpChild; - - public ModifiableCommandAddress() { - this.children = new LinkedHashMap<>(4); - } - - @Override - public boolean hasParent() { - return getParent() != null; - } - - @Override - public boolean hasCommand() { - return getCommand() != null; - } - - @Override - public boolean hasUserDeclaredCommand() { - Command command = getCommand(); - return command != null && !(command instanceof PredefinedCommand); - } - - @Override - public Command getCommand() { - return null; - } - - @Override - public boolean isRoot() { - return false; - } - - @Override - public List getNames() { - return null; - } - - @Override - public List getAliases() { - List names = getNames(); - if (names == null) { - return null; - } - if (names.isEmpty()) { - return Collections.emptyList(); - } - return names.subList(1, names.size()); - } - - @Override - public String getMainKey() { - return null; - } - - public void setCommand(Command command) { - throw new UnsupportedOperationException(); - } - - @Override - public abstract ModifiableCommandAddress getParent(); - - @Override - public RootCommandAddress getRoot() { - ModifiableCommandAddress out = this; - while (out.hasParent()) { - out = out.getParent(); - } - return out.isRoot() ? (RootCommandAddress) out : null; - } - - @Override - public int getDepth() { - int depth = 0; - ICommandAddress address = this; - while (address.hasParent()) { - address = address.getParent(); - depth++; - } - return depth; - } - - @Override - public boolean isDepthLargerThan(int value) { - int depth = 0; - ICommandAddress address = this; - do { - if (depth > value) { - return true; - } - - address = address.getParent(); - depth++; - } while (address != null); - return false; - } - - @Override - public boolean hasChildren() { - return !children.isEmpty(); - } - - @Override - public int getNumberOfRealChildren() { - return childrenMainKeys.size(); - } - - @Override - public Collection getChildrenMainKeys() { - return Collections.unmodifiableCollection(childrenMainKeys); - } - - @Override - public Map getChildren() { - return Collections.unmodifiableMap(children); - } - - @Override - public ChildCommandAddress getChild(String key) { - return children.get(key); - } - - @Override - public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return buffer.hasNext() ? getChild(buffer.next()) : null; - } - - public void addChild(ICommandAddress child) { - if (!(child instanceof ChildCommandAddress)) { - throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); - } - - ChildCommandAddress mChild = (ChildCommandAddress) child; - if (mChild.parent != null) { - throw new IllegalArgumentException("Argument already has a parent"); - } - - if (mChild.names.isEmpty()) { - throw new IllegalArgumentException("Argument must have names"); - } - - Iterator names = mChild.modifiableNamesIterator(); - String mainKey = names.next(); - - if (!childrenMainKeys.contains(mainKey)) { - if (!(childrenMainKeys instanceof ArrayList)) { - childrenMainKeys = new ArrayList<>(); - } - childrenMainKeys.add(mainKey); - } - - children.put(mainKey, mChild); - - while (names.hasNext()) { - String name = names.next(); - if (children.putIfAbsent(name, mChild) != null) { - names.remove(); - } - } - - mChild.setParent(this); - - if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) { - helpChild = mChild; - } - } - - public void removeChildren(boolean removeAliases, String... keys) { - if (keys.length == 0) { - throw new IllegalArgumentException("keys is empty"); - } - - for (String key : keys) { - ChildCommandAddress keyTarget = getChild(key); - if (keyTarget == null) { - continue; - } - - if (removeAliases) { - Iterator iterator = keyTarget.namesModifiable.iterator(); - boolean first = true; - while (iterator.hasNext()) { - String alias = iterator.next(); - ChildCommandAddress aliasTarget = getChild(key); - if (aliasTarget == keyTarget) { - if (first) { - childrenMainKeys.remove(alias); - } - children.remove(alias); - } - iterator.remove(); - first = false; - } - - } else { - if (key.equals(keyTarget.getMainKey())) { - childrenMainKeys.remove(key); - } - - children.remove(key); - keyTarget.namesModifiable.remove(key); - } - } - } - - public boolean hasHelpCommand() { - return helpChild != null; - } - - public ModifiableCommandAddress getHelpCommand() { - return helpChild; - } - - @Override - public IChatHandler getChatHandler() { - if (cachedChatHandlerFallback == null) { - if (chatHandler != null) { - cachedChatHandlerFallback = chatHandler; - } else if (!hasParent()) { - cachedChatHandlerFallback = ChatHandlers.defaultChat(); - } else { - cachedChatHandlerFallback = getParent().getChatHandler(); - } - } - return cachedChatHandlerFallback; - } - - public void setChatHandler(IChatHandler chatHandler) { - this.chatHandler = chatHandler; - resetChatHandlerCache(new HashSet<>()); - } - - void resetChatHandlerCache(Set dejaVu) { - if (dejaVu.add(this)) { - cachedChatHandlerFallback = chatHandler; - for (ChildCommandAddress address : children.values()) { - if (address.chatHandler == null) { - address.resetChatHandlerCache(dejaVu); - } - } - } - } - - @Override - public ICommandDispatcher getDispatcherForTree() { - return getRoot(); - } - - @Override - public boolean isCommandTrailing() { - return false; - } - - public void setCommandTrailing(boolean trailing) { - throw new UnsupportedOperationException(); - } - - void appendDebugInformation(StringBuilder target, String linePrefix, Set seen) { - target.append('\n').append(linePrefix); - if (!seen.add(this)) { - target.append(""); - return; - } - - if (this instanceof ChildCommandAddress) { - List namesModifiable = ((ChildCommandAddress) this).namesModifiable; - if (namesModifiable.isEmpty()) { - target.append(""); - } else { - Iterator keys = namesModifiable.iterator(); - target.append(keys.next()).append(' '); - if (keys.hasNext()) { - target.append('(').append(keys.next()); - while (keys.hasNext()) { - target.append(" ,").append(keys.next()); - } - target.append(") "); - } - } - } else { - target.append(" "); - } - - String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : ""; - target.append(commandClass); - - for (ChildCommandAddress child : new HashSet<>(children.values())) { - child.appendDebugInformation(target, linePrefix + " ", seen); - } - } - -} +package io.dico.dicore.command; + +import io.dico.dicore.command.chat.ChatHandlers; +import io.dico.dicore.command.chat.IChatHandler; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.ParameterList; +import io.dico.dicore.command.predef.DefaultGroupCommand; +import io.dico.dicore.command.predef.HelpCommand; +import io.dico.dicore.command.predef.PredefinedCommand; + +import java.util.*; + +public abstract class ModifiableCommandAddress implements ICommandAddress { + Map children; + Collection childrenMainKeys = Collections.emptyList(); + + // the chat handler as configured by the programmer + IChatHandler chatHandler; + // cache for the algorithm that finds the first chat handler going up the tree + transient IChatHandler cachedChatHandlerFallback; + + ModifiableCommandAddress helpChild; + + public ModifiableCommandAddress() { + this.children = new LinkedHashMap<>(4); + } + + @Override + public boolean hasParent() { + return getParent() != null; + } + + @Override + public boolean hasCommand() { + return getCommand() != null; + } + + @Override + public boolean hasUserDeclaredCommand() { + Command command = getCommand(); + return command != null && !(command instanceof PredefinedCommand); + } + + @Override + public Command getCommand() { + return null; + } + + @Override + public boolean isRoot() { + return false; + } + + @Override + public List getNames() { + return null; + } + + @Override + public List getAliases() { + List names = getNames(); + if (names == null) { + return null; + } + if (names.isEmpty()) { + return Collections.emptyList(); + } + return names.subList(1, names.size()); + } + + @Override + public String getMainKey() { + return null; + } + + public void setCommand(Command command) { + throw new UnsupportedOperationException(); + } + + @Override + public abstract ModifiableCommandAddress getParent(); + + @Override + public RootCommandAddress getRoot() { + ModifiableCommandAddress out = this; + while (out.hasParent()) { + out = out.getParent(); + } + return out.isRoot() ? (RootCommandAddress) out : null; + } + + @Override + public int getDepth() { + int depth = 0; + ICommandAddress address = this; + while (address.hasParent()) { + address = address.getParent(); + depth++; + } + return depth; + } + + @Override + public boolean isDepthLargerThan(int value) { + int depth = 0; + ICommandAddress address = this; + do { + if (depth > value) { + return true; + } + + address = address.getParent(); + depth++; + } while (address != null); + return false; + } + + @Override + public boolean hasChildren() { + return !children.isEmpty(); + } + + @Override + public int getNumberOfRealChildren() { + return childrenMainKeys.size(); + } + + @Override + public Collection getChildrenMainKeys() { + return Collections.unmodifiableCollection(childrenMainKeys); + } + + @Override + public Map getChildren() { + return Collections.unmodifiableMap(children); + } + + @Override + public ChildCommandAddress getChild(String key) { + return children.get(key); + } + + @Override + public ChildCommandAddress getChild(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + return buffer.hasNext() ? getChild(buffer.next()) : null; + } + + public void addChild(ICommandAddress child) { + if (!(child instanceof ChildCommandAddress)) { + throw new IllegalArgumentException("Argument must be a ChildCommandAddress"); + } + + ChildCommandAddress mChild = (ChildCommandAddress) child; + if (mChild.parent != null) { + throw new IllegalArgumentException("Argument already has a parent"); + } + + if (mChild.names.isEmpty()) { + throw new IllegalArgumentException("Argument must have names"); + } + + Iterator names = mChild.modifiableNamesIterator(); + String mainKey = names.next(); + + if (!childrenMainKeys.contains(mainKey)) { + if (!(childrenMainKeys instanceof ArrayList)) { + childrenMainKeys = new ArrayList<>(); + } + childrenMainKeys.add(mainKey); + } + + children.put(mainKey, mChild); + + while (names.hasNext()) { + String name = names.next(); + if (children.putIfAbsent(name, mChild) != null) { + names.remove(); + } + } + + mChild.setParent(this); + + if (mChild.hasCommand() && mChild.getCommand() instanceof HelpCommand) { + helpChild = mChild; + } + } + + public void removeChildren(boolean removeAliases, String... keys) { + if (keys.length == 0) { + throw new IllegalArgumentException("keys is empty"); + } + + for (String key : keys) { + ChildCommandAddress keyTarget = getChild(key); + if (keyTarget == null) { + continue; + } + + if (removeAliases) { + Iterator iterator = keyTarget.namesModifiable.iterator(); + boolean first = true; + while (iterator.hasNext()) { + String alias = iterator.next(); + ChildCommandAddress aliasTarget = getChild(key); + if (aliasTarget == keyTarget) { + if (first) { + childrenMainKeys.remove(alias); + } + children.remove(alias); + } + iterator.remove(); + first = false; + } + + } else { + if (key.equals(keyTarget.getMainKey())) { + childrenMainKeys.remove(key); + } + + children.remove(key); + keyTarget.namesModifiable.remove(key); + } + } + } + + public boolean hasHelpCommand() { + return helpChild != null; + } + + public ModifiableCommandAddress getHelpCommand() { + return helpChild; + } + + @Override + public IChatHandler getChatHandler() { + if (cachedChatHandlerFallback == null) { + if (chatHandler != null) { + cachedChatHandlerFallback = chatHandler; + } else if (!hasParent()) { + cachedChatHandlerFallback = ChatHandlers.defaultChat(); + } else { + cachedChatHandlerFallback = getParent().getChatHandler(); + } + } + return cachedChatHandlerFallback; + } + + public void setChatHandler(IChatHandler chatHandler) { + this.chatHandler = chatHandler; + resetChatHandlerCache(new HashSet<>()); + } + + void resetChatHandlerCache(Set dejaVu) { + if (dejaVu.add(this)) { + cachedChatHandlerFallback = chatHandler; + for (ChildCommandAddress address : children.values()) { + if (address.chatHandler == null) { + address.resetChatHandlerCache(dejaVu); + } + } + } + } + + @Override + public ICommandDispatcher getDispatcherForTree() { + return getRoot(); + } + + @Override + public boolean isCommandTrailing() { + return false; + } + + public void setCommandTrailing(boolean trailing) { + throw new UnsupportedOperationException(); + } + + void appendDebugInformation(StringBuilder target, String linePrefix, Set seen) { + target.append('\n').append(linePrefix); + if (!seen.add(this)) { + target.append(""); + return; + } + + if (this instanceof ChildCommandAddress) { + List namesModifiable = ((ChildCommandAddress) this).namesModifiable; + if (namesModifiable.isEmpty()) { + target.append(""); + } else { + Iterator keys = namesModifiable.iterator(); + target.append(keys.next()).append(' '); + if (keys.hasNext()) { + target.append('(').append(keys.next()); + while (keys.hasNext()) { + target.append(" ,").append(keys.next()); + } + target.append(") "); + } + } + } else { + target.append(" "); + } + + String commandClass = hasCommand() ? getCommand().getClass().getCanonicalName() : ""; + target.append(commandClass); + + for (ChildCommandAddress child : new HashSet<>(children.values())) { + child.appendDebugInformation(target, linePrefix + " ", seen); + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java index 75b2035..c996356 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/PermissionContextFilter.java @@ -1,136 +1,136 @@ -package io.dico.dicore.command; - -import java.util.List; -import java.util.Objects; - -public class PermissionContextFilter implements IContextFilter { - private String permission; - private String[] permissionComponents; - private int componentInsertionIndex; - private String failMessage; - - public PermissionContextFilter(String permission) { - this.permission = Objects.requireNonNull(permission); - } - - public PermissionContextFilter(String permission, String failMessage) { - this(permission); - this.failMessage = failMessage; - } - - public PermissionContextFilter(String permission, boolean inheritable) { - this(permission, null, inheritable); - } - - public PermissionContextFilter(String permission, String failMessage, boolean inheritable) { - this(permission, failMessage); - if (inheritable) { - setupInheritability(-1); - } - } - - public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) { - this(permission, failMessage); - setupInheritability(componentInsertionIndex); - } - - private void setupInheritability(int componentInsertionIndex) { - this.permissionComponents = permission.split("\\."); - this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex; - if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException(); - } - - private void doFilter(ExecutionContext context, String permission) throws CommandException { - if (failMessage != null) { - Validate.isAuthorized(context.getSender(), permission, failMessage); - } else { - Validate.isAuthorized(context.getSender(), permission); - } - } - - @Override - public void filterContext(ExecutionContext context) throws CommandException { - doFilter(context, permission); - } - - public String getInheritedPermission(String[] components) { - int insertedAmount = components.length; - String[] currentComponents = permissionComponents; - int currentAmount = currentComponents.length; - String[] targetArray = new String[currentAmount + insertedAmount]; - - int insertionIndex; - //int newInsertionIndex; - if (componentInsertionIndex == -1) { - insertionIndex = currentAmount; - //newInsertionIndex = -1; - } else { - insertionIndex = componentInsertionIndex; - //newInsertionIndex = insertionIndex + insertedAmount; - } - - // copy the current components up to insertionIndex - System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex); - // copy the new components into the array at insertionIndex - System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount); - // copy the current components from insertionIndex + inserted amount - System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex); - - return String.join(".", targetArray); - } - - @Override - public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { - if (isInheritable()) { - doFilter(subContext, getInheritedPermission(path)); - } - } - - @Override - public Priority getPriority() { - return Priority.PERMISSION; - } - - public boolean isInheritable() { - return permissionComponents != null; - } - - public String getPermission() { - return permission; - } - - public int getComponentInsertionIndex() { - return componentInsertionIndex; - } - - public String getFailMessage() { - return failMessage; - } - - /* - private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf()) - - private fun getPermissionsOf(address: ICommandAddress, path: Array, result: MutableList): List { - val command = address.command ?: return result - - var inherited = false - for (filter in command.contextFilters) { - when (filter) { - is PermissionContextFilter -> { - if (path.isEmpty()) result.add(filter.permission) - else if (filter.isInheritable) result.add(filter.getInheritedPermission(path)) - } - is InheritingContextFilter -> { - if (filter.priority == PERMISSION && address.hasParent() && !inherited) { - inherited = true - getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result) - } - } - } - } - - return result - } - */ - -} +package io.dico.dicore.command; + +import java.util.List; +import java.util.Objects; + +public class PermissionContextFilter implements IContextFilter { + private String permission; + private String[] permissionComponents; + private int componentInsertionIndex; + private String failMessage; + + public PermissionContextFilter(String permission) { + this.permission = Objects.requireNonNull(permission); + } + + public PermissionContextFilter(String permission, String failMessage) { + this(permission); + this.failMessage = failMessage; + } + + public PermissionContextFilter(String permission, boolean inheritable) { + this(permission, null, inheritable); + } + + public PermissionContextFilter(String permission, String failMessage, boolean inheritable) { + this(permission, failMessage); + if (inheritable) { + setupInheritability(-1); + } + } + + public PermissionContextFilter(String permission, int componentInsertionIndex, String failMessage) { + this(permission, failMessage); + setupInheritability(componentInsertionIndex); + } + + private void setupInheritability(int componentInsertionIndex) { + this.permissionComponents = permission.split("\\."); + this.componentInsertionIndex = componentInsertionIndex < 0 ? permissionComponents.length : componentInsertionIndex; + if (componentInsertionIndex > permissionComponents.length) throw new IllegalArgumentException(); + } + + private void doFilter(ExecutionContext context, String permission) throws CommandException { + if (failMessage != null) { + Validate.isAuthorized(context.getSender(), permission, failMessage); + } else { + Validate.isAuthorized(context.getSender(), permission); + } + } + + @Override + public void filterContext(ExecutionContext context) throws CommandException { + doFilter(context, permission); + } + + public String getInheritedPermission(String[] components) { + int insertedAmount = components.length; + String[] currentComponents = permissionComponents; + int currentAmount = currentComponents.length; + String[] targetArray = new String[currentAmount + insertedAmount]; + + int insertionIndex; + //int newInsertionIndex; + if (componentInsertionIndex == -1) { + insertionIndex = currentAmount; + //newInsertionIndex = -1; + } else { + insertionIndex = componentInsertionIndex; + //newInsertionIndex = insertionIndex + insertedAmount; + } + + // copy the current components up to insertionIndex + System.arraycopy(currentComponents, 0, targetArray, 0, insertionIndex); + // copy the new components into the array at insertionIndex + System.arraycopy(components, 0, targetArray, insertionIndex, insertedAmount); + // copy the current components from insertionIndex + inserted amount + System.arraycopy(currentComponents, insertionIndex, targetArray, insertionIndex + insertedAmount, currentAmount - insertionIndex); + + return String.join(".", targetArray); + } + + @Override + public void filterSubContext(ExecutionContext subContext, String... path) throws CommandException { + if (isInheritable()) { + doFilter(subContext, getInheritedPermission(path)); + } + } + + @Override + public Priority getPriority() { + return Priority.PERMISSION; + } + + public boolean isInheritable() { + return permissionComponents != null; + } + + public String getPermission() { + return permission; + } + + public int getComponentInsertionIndex() { + return componentInsertionIndex; + } + + public String getFailMessage() { + return failMessage; + } + + /* + private fun getPermissionsOf(address: ICommandAddress) = getPermissionsOf(address, emptyArray(), mutableListOf()) + + private fun getPermissionsOf(address: ICommandAddress, path: Array, result: MutableList): List { + val command = address.command ?: return result + + var inherited = false + for (filter in command.contextFilters) { + when (filter) { + is PermissionContextFilter -> { + if (path.isEmpty()) result.add(filter.permission) + else if (filter.isInheritable) result.add(filter.getInheritedPermission(path)) + } + is InheritingContextFilter -> { + if (filter.priority == PERMISSION && address.hasParent() && !inherited) { + inherited = true + getPermissionsOf(address.parent, arrayOf(address.mainKey, *path), result) + } + } + } + } + + return result + } + */ + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java index 44f0540..2b70eaf 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/RootCommandAddress.java @@ -1,275 +1,275 @@ -package io.dico.dicore.command; - -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.registration.BukkitCommand; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.*; - -public class RootCommandAddress extends ModifiableCommandAddress implements ICommandDispatcher { - @Deprecated - public static final RootCommandAddress INSTANCE = new RootCommandAddress(); - - public RootCommandAddress() { - } - - @Override - public Command getCommand() { - return null; - } - - @Override - public boolean isRoot() { - return true; - } - - @Override - public List getNames() { - return Collections.emptyList(); - } - - @Override - public ModifiableCommandAddress getParent() { - return null; - } - - @Override - public String getMainKey() { - return null; - } - - @Override - public String getAddress() { - return ""; - } - - @Override - public void registerToCommandMap(String fallbackPrefix, Map map, EOverridePolicy overridePolicy) { - Objects.requireNonNull(overridePolicy); - //debugChildren(this); - Map children = this.children; - Map wrappers = new IdentityHashMap<>(); - - for (ChildCommandAddress address : children.values()) { - if (!wrappers.containsKey(address)) { - wrappers.put(address, new BukkitCommand(address)); - } - } - - for (Map.Entry entry : children.entrySet()) { - String key = entry.getKey(); - ChildCommandAddress address = entry.getValue(); - boolean override = overridePolicy == EOverridePolicy.OVERRIDE_ALL; - if (!override && key.equals(address.getMainKey())) { - override = overridePolicy == EOverridePolicy.MAIN_KEY_ONLY || overridePolicy == EOverridePolicy.MAIN_AND_FALLBACK; - } - - registerMember(map, key, wrappers.get(address), override); - - if (fallbackPrefix != null) { - key = fallbackPrefix + key; - override = overridePolicy != EOverridePolicy.OVERRIDE_NONE && overridePolicy != EOverridePolicy.MAIN_KEY_ONLY; - registerMember(map, key, wrappers.get(address), override); - } - } - - } - - private static void debugChildren(ModifiableCommandAddress address) { - Collection keys = address.getChildrenMainKeys(); - for (String key : keys) { - ChildCommandAddress child = address.getChild(key); - System.out.println(child.getAddress()); - debugChildren(child); - } - } - - private static void registerMember(Map map, - String key, org.bukkit.command.Command value, boolean override) { - if (override) { - map.put(key, value); - } else { - map.putIfAbsent(key, value); - } - } - - @Override - public void unregisterFromCommandMap(Map map) { - Set children = new HashSet<>(this.children.values()); - Iterator> iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - org.bukkit.command.Command cmd = entry.getValue(); - if (cmd instanceof BukkitCommand && children.contains(((BukkitCommand) cmd).getOrigin())) { - iterator.remove(); - } - } - } - - @Override - public ModifiableCommandAddress getDeepChild(ArgumentBuffer buffer) { - ModifiableCommandAddress cur = this; - ChildCommandAddress child; - while (buffer.hasNext()) { - child = cur.getChild(buffer.next()); - if (child == null) { - buffer.rewind(); - return cur; - } - - cur = child; - } - return cur; - } - - @Override - public ModifiableCommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer) { - ModifiableCommandAddress cur = this; - ChildCommandAddress child; - while (buffer.hasNext()) { - child = cur.getChild(buffer.next()); - if (child == null - || (child.hasCommand() && !child.getCommand().isVisibleTo(sender)) - || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) { - buffer.rewind(); - break; - } - - cur = child; - } - - return cur; - } - - @Override - public ModifiableCommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - CommandSender sender = context.getSender(); - ModifiableCommandAddress cur = this; - ChildCommandAddress child; - while (buffer.hasNext()) { - int cursor = buffer.getCursor(); - - child = cur.getChild(context, buffer); - - if (child == null - || (context.isTabComplete() && !buffer.hasNext()) - || (child.hasCommand() && !child.getCommand().isVisibleTo(sender)) - || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) { - buffer.setCursor(cursor); - break; - } - - cur = child; - - context.setAddress(child); - if (child.hasCommand() && child.isCommandTrailing()) { - child.getCommand().initializeAndFilterContext(context); - child.getCommand().execute(context.getSender(), context); - } - } - - return cur; - } - - @Override - public boolean dispatchCommand(CommandSender sender, String[] command) { - return dispatchCommand(sender, new ArgumentBuffer(command)); - } - - @Override - public boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args) { - return dispatchCommand(sender, new ArgumentBuffer(usedLabel, args)); - } - - @Override - public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) { - ExecutionContext context = new ExecutionContext(sender, buffer, false); - - ModifiableCommandAddress targetAddress = null; - - try { - targetAddress = getCommandTarget(context, buffer); - Command target = targetAddress.getCommand(); - - if (target == null) { - if (targetAddress.hasHelpCommand()) { - target = targetAddress.getHelpCommand().getCommand(); - } else { - return false; - } - } - - context.setCommand(target); - - if (!targetAddress.isCommandTrailing()) { - target.initializeAndFilterContext(context); - String message = target.execute(sender, context); - if (message != null && !message.isEmpty()) { - context.sendMessage(EMessageType.RESULT, message); - } - } - - } catch (Throwable t) { - if (targetAddress == null) { - targetAddress = this; - } - targetAddress.getChatHandler().handleException(sender, context, t); - } - - return true; - } - - @Override - public List getTabCompletions(CommandSender sender, Location location, String[] args) { - return getTabCompletions(sender, location, new ArgumentBuffer(args)); - } - - @Override - public List getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args) { - return getTabCompletions(sender, location, new ArgumentBuffer(usedLabel, args)); - } - - @Override - public List getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) { - ExecutionContext context = new ExecutionContext(sender, buffer, true); - - try { - ICommandAddress target = getCommandTarget(context, buffer); - - List out; - if (target.hasCommand()) { - context.setCommand(target.getCommand()); - target.getCommand().initializeAndFilterContext(context); - out = target.getCommand().tabComplete(sender, context, location); - } else { - out = Collections.emptyList(); - } - - int cursor = buffer.getCursor(); - String input; - if (cursor >= buffer.size()) { - input = ""; - } else { - input = buffer.get(cursor).toLowerCase(); - } - - boolean wrapped = false; - for (String child : target.getChildrenMainKeys()) { - if (child.toLowerCase().startsWith(input)) { - if (!wrapped) { - out = new ArrayList<>(out); - wrapped = true; - } - out.add(child); - } - } - - return out; - - } catch (CommandException ex) { - return Collections.emptyList(); - } - - } -} +package io.dico.dicore.command; + +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.registration.BukkitCommand; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.*; + +public class RootCommandAddress extends ModifiableCommandAddress implements ICommandDispatcher { + @Deprecated + public static final RootCommandAddress INSTANCE = new RootCommandAddress(); + + public RootCommandAddress() { + } + + @Override + public Command getCommand() { + return null; + } + + @Override + public boolean isRoot() { + return true; + } + + @Override + public List getNames() { + return Collections.emptyList(); + } + + @Override + public ModifiableCommandAddress getParent() { + return null; + } + + @Override + public String getMainKey() { + return null; + } + + @Override + public String getAddress() { + return ""; + } + + @Override + public void registerToCommandMap(String fallbackPrefix, Map map, EOverridePolicy overridePolicy) { + Objects.requireNonNull(overridePolicy); + //debugChildren(this); + Map children = this.children; + Map wrappers = new IdentityHashMap<>(); + + for (ChildCommandAddress address : children.values()) { + if (!wrappers.containsKey(address)) { + wrappers.put(address, new BukkitCommand(address)); + } + } + + for (Map.Entry entry : children.entrySet()) { + String key = entry.getKey(); + ChildCommandAddress address = entry.getValue(); + boolean override = overridePolicy == EOverridePolicy.OVERRIDE_ALL; + if (!override && key.equals(address.getMainKey())) { + override = overridePolicy == EOverridePolicy.MAIN_KEY_ONLY || overridePolicy == EOverridePolicy.MAIN_AND_FALLBACK; + } + + registerMember(map, key, wrappers.get(address), override); + + if (fallbackPrefix != null) { + key = fallbackPrefix + key; + override = overridePolicy != EOverridePolicy.OVERRIDE_NONE && overridePolicy != EOverridePolicy.MAIN_KEY_ONLY; + registerMember(map, key, wrappers.get(address), override); + } + } + + } + + private static void debugChildren(ModifiableCommandAddress address) { + Collection keys = address.getChildrenMainKeys(); + for (String key : keys) { + ChildCommandAddress child = address.getChild(key); + System.out.println(child.getAddress()); + debugChildren(child); + } + } + + private static void registerMember(Map map, + String key, org.bukkit.command.Command value, boolean override) { + if (override) { + map.put(key, value); + } else { + map.putIfAbsent(key, value); + } + } + + @Override + public void unregisterFromCommandMap(Map map) { + Set children = new HashSet<>(this.children.values()); + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + org.bukkit.command.Command cmd = entry.getValue(); + if (cmd instanceof BukkitCommand && children.contains(((BukkitCommand) cmd).getOrigin())) { + iterator.remove(); + } + } + } + + @Override + public ModifiableCommandAddress getDeepChild(ArgumentBuffer buffer) { + ModifiableCommandAddress cur = this; + ChildCommandAddress child; + while (buffer.hasNext()) { + child = cur.getChild(buffer.next()); + if (child == null) { + buffer.rewind(); + return cur; + } + + cur = child; + } + return cur; + } + + @Override + public ModifiableCommandAddress getCommandTarget(CommandSender sender, ArgumentBuffer buffer) { + ModifiableCommandAddress cur = this; + ChildCommandAddress child; + while (buffer.hasNext()) { + child = cur.getChild(buffer.next()); + if (child == null + || (child.hasCommand() && !child.getCommand().isVisibleTo(sender)) + || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) { + buffer.rewind(); + break; + } + + cur = child; + } + + return cur; + } + + @Override + public ModifiableCommandAddress getCommandTarget(ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + CommandSender sender = context.getSender(); + ModifiableCommandAddress cur = this; + ChildCommandAddress child; + while (buffer.hasNext()) { + int cursor = buffer.getCursor(); + + child = cur.getChild(context, buffer); + + if (child == null + || (context.isTabComplete() && !buffer.hasNext()) + || (child.hasCommand() && !child.getCommand().isVisibleTo(sender)) + || (cur.hasCommand() && cur.getCommand().takePrecedenceOverSubcommand(buffer.peekPrevious(), buffer.getUnaffectingCopy()))) { + buffer.setCursor(cursor); + break; + } + + cur = child; + + context.setAddress(child); + if (child.hasCommand() && child.isCommandTrailing()) { + child.getCommand().initializeAndFilterContext(context); + child.getCommand().execute(context.getSender(), context); + } + } + + return cur; + } + + @Override + public boolean dispatchCommand(CommandSender sender, String[] command) { + return dispatchCommand(sender, new ArgumentBuffer(command)); + } + + @Override + public boolean dispatchCommand(CommandSender sender, String usedLabel, String[] args) { + return dispatchCommand(sender, new ArgumentBuffer(usedLabel, args)); + } + + @Override + public boolean dispatchCommand(CommandSender sender, ArgumentBuffer buffer) { + ExecutionContext context = new ExecutionContext(sender, buffer, false); + + ModifiableCommandAddress targetAddress = null; + + try { + targetAddress = getCommandTarget(context, buffer); + Command target = targetAddress.getCommand(); + + if (target == null) { + if (targetAddress.hasHelpCommand()) { + target = targetAddress.getHelpCommand().getCommand(); + } else { + return false; + } + } + + context.setCommand(target); + + if (!targetAddress.isCommandTrailing()) { + target.initializeAndFilterContext(context); + String message = target.execute(sender, context); + if (message != null && !message.isEmpty()) { + context.sendMessage(EMessageType.RESULT, message); + } + } + + } catch (Throwable t) { + if (targetAddress == null) { + targetAddress = this; + } + targetAddress.getChatHandler().handleException(sender, context, t); + } + + return true; + } + + @Override + public List getTabCompletions(CommandSender sender, Location location, String[] args) { + return getTabCompletions(sender, location, new ArgumentBuffer(args)); + } + + @Override + public List getTabCompletions(CommandSender sender, String usedLabel, Location location, String[] args) { + return getTabCompletions(sender, location, new ArgumentBuffer(usedLabel, args)); + } + + @Override + public List getTabCompletions(CommandSender sender, Location location, ArgumentBuffer buffer) { + ExecutionContext context = new ExecutionContext(sender, buffer, true); + + try { + ICommandAddress target = getCommandTarget(context, buffer); + + List out; + if (target.hasCommand()) { + context.setCommand(target.getCommand()); + target.getCommand().initializeAndFilterContext(context); + out = target.getCommand().tabComplete(sender, context, location); + } else { + out = Collections.emptyList(); + } + + int cursor = buffer.getCursor(); + String input; + if (cursor >= buffer.size()) { + input = ""; + } else { + input = buffer.get(cursor).toLowerCase(); + } + + boolean wrapped = false; + for (String child : target.getChildrenMainKeys()) { + if (child.toLowerCase().startsWith(input)) { + if (!wrapped) { + out = new ArrayList<>(out); + wrapped = true; + } + out.add(child); + } + } + + return out; + + } catch (CommandException ex) { + return Collections.emptyList(); + } + + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java index 596ad08..3b1f7d4 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/Validate.java @@ -1,52 +1,52 @@ -package io.dico.dicore.command; - -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.util.Optional; - -public class Validate { - - private Validate() { - - } - - //@Contract("false, _ -> fail") - public static void isTrue(boolean expression, String failMessage) throws CommandException { - if (!expression) { - throw new CommandException(failMessage); - } - } - - //@Contract("null, _ -> fail") - public static void notNull(Object obj, String failMessage) throws CommandException { - Validate.isTrue(obj != null, failMessage); - } - - public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException { - Validate.isTrue(sender.hasPermission(permission), failMessage); - } - - public static void isAuthorized(CommandSender sender, String permission) throws CommandException { - Validate.isAuthorized(sender, permission, "You do not have permission to use that command"); - } - - //@Contract("null -> fail") - public static void isPlayer(CommandSender sender) throws CommandException { - isTrue(sender instanceof Player, "That command can only be used by players"); - } - - //@Contract("null -> fail") - public static void isConsole(CommandSender sender) throws CommandException { - isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console"); - } - - public static T returnIfPresent(Optional maybe, String failMessage) throws CommandException { - if (!maybe.isPresent()) { - throw new CommandException(failMessage); - } - return maybe.get(); - } - -} +package io.dico.dicore.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.util.Optional; + +public class Validate { + + private Validate() { + + } + + //@Contract("false, _ -> fail") + public static void isTrue(boolean expression, String failMessage) throws CommandException { + if (!expression) { + throw new CommandException(failMessage); + } + } + + //@Contract("null, _ -> fail") + public static void notNull(Object obj, String failMessage) throws CommandException { + Validate.isTrue(obj != null, failMessage); + } + + public static void isAuthorized(CommandSender sender, String permission, String failMessage) throws CommandException { + Validate.isTrue(sender.hasPermission(permission), failMessage); + } + + public static void isAuthorized(CommandSender sender, String permission) throws CommandException { + Validate.isAuthorized(sender, permission, "You do not have permission to use that command"); + } + + //@Contract("null -> fail") + public static void isPlayer(CommandSender sender) throws CommandException { + isTrue(sender instanceof Player, "That command can only be used by players"); + } + + //@Contract("null -> fail") + public static void isConsole(CommandSender sender) throws CommandException { + isTrue(sender instanceof ConsoleCommandSender, "That command can only be used by the console"); + } + + public static T returnIfPresent(Optional maybe, String failMessage) throws CommandException { + if (!maybe.isPresent()) { + throw new CommandException(failMessage); + } + return maybe.get(); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java index 467ba4b..b0277c6 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/BigRange.java @@ -1,52 +1,52 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.parameter.type.ParameterConfig; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface BigRange { - Class MEMORY_CLASS = Memory.class; - ParameterConfig CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class); - Memory DEFAULT = new Memory("MIN", "MAX", "0"); - - String min() default "MIN"; - - String max() default "MAX"; - - String defaultValue() default "0"; - - class Memory { - private final String min; - private final String max; - private final String defaultValue; - - public Memory(BigRange range) { - this(range.min(), range.max(), range.defaultValue()); - } - - public Memory(String min, String max, String defaultValue) { - this.min = min; - this.max = max; - this.defaultValue = defaultValue; - } - - public String min() { - return min; - } - - public String max() { - return max; - } - - public String defaultValue() { - return defaultValue; - } - - } - -} +package io.dico.dicore.command.annotation; + +import io.dico.dicore.command.parameter.type.ParameterConfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface BigRange { + Class MEMORY_CLASS = Memory.class; + ParameterConfig CONFIG = ParameterConfig.getMemoryClassFromField(BigRange.class); + Memory DEFAULT = new Memory("MIN", "MAX", "0"); + + String min() default "MIN"; + + String max() default "MAX"; + + String defaultValue() default "0"; + + class Memory { + private final String min; + private final String max; + private final String defaultValue; + + public Memory(BigRange range) { + this(range.min(), range.max(), range.defaultValue()); + } + + public Memory(String min, String max, String defaultValue) { + this.min = min; + this.max = max; + this.defaultValue = defaultValue; + } + + public String min() { + return min; + } + + public String max() { + return max; + } + + public String defaultValue() { + return defaultValue; + } + + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java index 109490a..dfad0bb 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Cmd.java @@ -1,16 +1,16 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Cmd { - - String value(); - - String[] aliases() default {}; - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Cmd { + + String value(); + + String[] aliases() default {}; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java index ea51e44..3eb2d53 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CmdParamType.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface CmdParamType { - - /** - * 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). - * 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 - */ - boolean infolessAlias() default false; - -} - +package io.dico.dicore.command.annotation; + +import io.dico.dicore.command.parameter.type.IParameterTypeSelector; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark methods that register a parameter type to the localized selector for use in reflective commands. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface CmdParamType { + + /** + * 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). + * 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 + */ + boolean infolessAlias() default false; + +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java index 868884c..7983997 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/CommandAnnotationUtils.java @@ -1,35 +1,35 @@ -package io.dico.dicore.command.annotation; - -public class CommandAnnotationUtils { - - /** - * Get the short description from a {@link Desc} annotation. - * If {@link Desc#shortVersion()} is given, returns that. - * Otherwise, returns the first element of {@link Desc#value()} - * If neither is available, returns null. - * - * @param desc the annotation - * @return the short description - */ - public static String getShortDescription(Desc desc) { - String descString; - if (desc == null) { - descString = null; - } else if (!desc.shortVersion().isEmpty()) { - descString = desc.shortVersion(); - } else if (desc.value().length > 0) { - descString = desc.value()[0]; - if (desc.value().length > 1) { - //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier); - } - if (descString != null && descString.isEmpty()) { - descString = null; - } - } else { - descString = null; - } - - return descString; - } - -} +package io.dico.dicore.command.annotation; + +public class CommandAnnotationUtils { + + /** + * Get the short description from a {@link Desc} annotation. + * If {@link Desc#shortVersion()} is given, returns that. + * Otherwise, returns the first element of {@link Desc#value()} + * If neither is available, returns null. + * + * @param desc the annotation + * @return the short description + */ + public static String getShortDescription(Desc desc) { + String descString; + if (desc == null) { + descString = null; + } else if (!desc.shortVersion().isEmpty()) { + descString = desc.shortVersion(); + } else if (desc.value().length > 0) { + descString = desc.value()[0]; + if (desc.value().length > 1) { + //System.out.println("[Command Warning] Multiline descriptions not supported here. Keep it short for: " + targetIdentifier); + } + if (descString != null && descString.isEmpty()) { + descString = null; + } + } else { + descString = null; + } + + return descString; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java index 0011fb8..ab3e555 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Desc.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface Desc { - - /** - * 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). - * - * @return the multiline description. - * @see CommandAnnotationUtils#getShortDescription(Desc) - */ - String[] value(); - - /** - * 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)} - * - * @return short description - * @see CommandAnnotationUtils#getShortDescription(Desc) - */ - String shortVersion() default ""; - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Desc { + + /** + * 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). + * + * @return the multiline description. + * @see CommandAnnotationUtils#getShortDescription(Desc) + */ + String[] value(); + + /** + * 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)} + * + * @return short description + * @see CommandAnnotationUtils#getShortDescription(Desc) + */ + String shortVersion() default ""; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java index 31a47dd..edc50ef 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Flag.java @@ -1,16 +1,16 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Flag { - - String value() default ""; - - String permission() default ""; - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Flag { + + String value() default ""; + + String permission() default ""; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java index 9b7164d..eeff351 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GenerateCommands.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface GenerateCommands { - - String[] value() default {"help"}; - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface GenerateCommands { + + String[] value() default {"help"}; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java index 53e3e9e..de89e00 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/GroupMatchedCommands.java @@ -1,68 +1,68 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to define sub-groups of the group registered reflectively from all methods in a class. - *

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

+ * Commands are selected for grouping by matching their method's names to a regular expression. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface GroupMatchedCommands { + + @Retention(RetentionPolicy.RUNTIME) + @interface GroupEntry { + + /** + * Regular expression to match method names for this group + * Must be non-empty + * + * @return the regular expression + */ + String regex(); + + /** + * The name or main key of the sub-group or address + * Must be non-empty + * + * @return the group name + */ + String group(); + + /** + * The aliases for the sub-group + * + * @return the group aliases + */ + String[] groupAliases() default {}; + + /** + * Generated (predefined) commands for the sub-group + */ + String[] generatedCommands() default {}; + + /** + * @see Desc + */ + String[] description() default {}; + + /** + * @see Desc + */ + String shortDescription() default ""; + } + + /** + * The defined groups. + * If a method name matches the regex of multiple groups, + * groups are prioritized by the order in which they appear in this array. + * + * @return the defined groups + */ + GroupEntry[] value(); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java index fa42e6b..d84358b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/NamedArg.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface NamedArg { - - String value(); - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface NamedArg { + + String value(); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java index 57f53bd..32b0723 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/PreprocessArgs.java @@ -1,17 +1,17 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -@Deprecated -public @interface PreprocessArgs { - - String tokens() default "\"\""; - - char escapeChar() default '\\'; - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Deprecated +public @interface PreprocessArgs { + + String tokens() default "\"\""; + + char escapeChar() default '\\'; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java index 3fd4160..98aec22 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/Range.java @@ -1,67 +1,67 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.Validate; -import io.dico.dicore.command.parameter.type.ParameterConfig; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Range { - Class MEMORY_CLASS = Memory.class; - ParameterConfig CONFIG = ParameterConfig.getMemoryClassFromField(Range.class); - Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0); - - double min() default -Double.MAX_VALUE; - - double max() default Double.MAX_VALUE; - - double defaultValue() default 0; - - class Memory { - private final double min; - private final double max; - private final double defaultValue; - - public Memory(Range range) { - this(range.min(), range.max(), range.defaultValue()); - } - - public Memory(double min, double max, double defaultValue) { - this.min = min; - this.max = max; - this.defaultValue = defaultValue; - } - - public double min() { - return min; - } - - public double max() { - return max; - } - - public double defaultValue() { - return defaultValue; - } - - public void validate(Number x, String failMessage) throws CommandException { - Validate.isTrue(valid(x), failMessage); - } - - public boolean valid(Number x) { - double d = x.doubleValue(); - return min <= d && d <= max; - } - - public boolean isDefault() { - return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue); - } - - } - -} +package io.dico.dicore.command.annotation; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.Validate; +import io.dico.dicore.command.parameter.type.ParameterConfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Range { + Class MEMORY_CLASS = Memory.class; + ParameterConfig CONFIG = ParameterConfig.getMemoryClassFromField(Range.class); + Memory DEFAULT = new Memory(-Double.MAX_VALUE, Double.MAX_VALUE, 0); + + double min() default -Double.MAX_VALUE; + + double max() default Double.MAX_VALUE; + + double defaultValue() default 0; + + class Memory { + private final double min; + private final double max; + private final double defaultValue; + + public Memory(Range range) { + this(range.min(), range.max(), range.defaultValue()); + } + + public Memory(double min, double max, double defaultValue) { + this.min = min; + this.max = max; + this.defaultValue = defaultValue; + } + + public double min() { + return min; + } + + public double max() { + return max; + } + + public double defaultValue() { + return defaultValue; + } + + public void validate(Number x, String failMessage) throws CommandException { + Validate.isTrue(valid(x), failMessage); + } + + public boolean valid(Number x) { + double d = x.doubleValue(); + return min <= d && d <= max; + } + + public boolean isDefault() { + return this == DEFAULT || (min == DEFAULT.min && max == DEFAULT.max && defaultValue == DEFAULT.defaultValue); + } + + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java index 362f05c..3510ca8 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireConsole.java @@ -1,11 +1,11 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequireConsole { -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequireConsole { +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java index 02f5548..68d5706 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequireParameters.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequireParameters { - - int value(); - -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequireParameters { + + int value(); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java index d2ba782..13d7167 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePermissions.java @@ -1,33 +1,33 @@ -package io.dico.dicore.command.annotation; - -import io.dico.dicore.command.IContextFilter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * If this annotation is not present, inheriting permissions is default. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequirePermissions { - - /** - * Any permissions that must be present on the sender - * - * @return an array of permission nodes - */ - String[] value(); - - /** - * Whether permissions should (also) be inherited from the parent. - * This uses {@link IContextFilter#INHERIT_PERMISSIONS} - * This is true by default. - * - * @return true if permissions should be inherited. - */ - boolean inherit() default true; - -} +package io.dico.dicore.command.annotation; + +import io.dico.dicore.command.IContextFilter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * If this annotation is not present, inheriting permissions is default. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequirePermissions { + + /** + * Any permissions that must be present on the sender + * + * @return an array of permission nodes + */ + String[] value(); + + /** + * Whether permissions should (also) be inherited from the parent. + * This uses {@link IContextFilter#INHERIT_PERMISSIONS} + * This is true by default. + * + * @return true if permissions should be inherited. + */ + boolean inherit() default true; + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java index 2165e05..3f447f9 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/annotation/RequirePlayer.java @@ -1,11 +1,11 @@ -package io.dico.dicore.command.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface RequirePlayer { -} +package io.dico.dicore.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RequirePlayer { +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java index c0d23ee..ac2a76d 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/AbstractChatHandler.java @@ -1,94 +1,94 @@ -package io.dico.dicore.command.chat; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.HelpPages; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public class AbstractChatHandler implements IChatHandler { - private @NotNull HelpPages helpPages; - - public AbstractChatHandler(@NotNull HelpPages helpPages) { - this.helpPages = helpPages; - } - - public AbstractChatHandler() { - this(HelpPages.newDefaultHelpPages()); - } - - @NotNull - public HelpPages getHelpPages() { - return helpPages; - } - - public void setHelpPages(@NotNull HelpPages helpPages) { - this.helpPages = helpPages; - } - - @Override - public Formatting getChatFormatForType(EMessageType type) { - switch (type) { - case EXCEPTION: - case BAD_NEWS: - return Formatting.RED; - case INSTRUCTION: - case NEUTRAL: - return Formatting.GRAY; - case CUSTOM: - return Formatting.WHITE; - case INFORMATIVE: - return Formatting.AQUA; - case RESULT: - default: - case GOOD_NEWS: - return Formatting.GREEN; - case WARNING: - return Formatting.YELLOW; - - case DESCRIPTION: - return Formatting.GREEN; - case SYNTAX: - return Formatting.AQUA; - case HIGHLIGHT: - return Formatting.RED; - case SUBCOMMAND: - return Formatting.GRAY; - case NUMBER: - return Formatting.YELLOW; - } - } - - @Override - public String getMessagePrefixForType(EMessageType type) { - return ""; - } - - protected String createMessage(EMessageType type, String message) { - if (message == null || message.isEmpty()) return null; - return getMessagePrefixForType(type) + getChatFormatForType(type) + message; - } - - @Override - public String createMessage(ExecutionContext context, EMessageType type, String message) { - return createMessage(type, message); - } - - @Override - public String createMessage(CommandSender sender, EMessageType type, String message) { - return createMessage(type, message); - } - - @Override - public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { - return helpPages.getHelpPage(sender, context, address, page); - } - - @Override - public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { - return helpPages.getSyntax(sender, context, address); - } - -} +package io.dico.dicore.command.chat; + +import io.dico.dicore.Formatting; +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.HelpPages; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class AbstractChatHandler implements IChatHandler { + private @NotNull HelpPages helpPages; + + public AbstractChatHandler(@NotNull HelpPages helpPages) { + this.helpPages = helpPages; + } + + public AbstractChatHandler() { + this(HelpPages.newDefaultHelpPages()); + } + + @NotNull + public HelpPages getHelpPages() { + return helpPages; + } + + public void setHelpPages(@NotNull HelpPages helpPages) { + this.helpPages = helpPages; + } + + @Override + public Formatting getChatFormatForType(EMessageType type) { + switch (type) { + case EXCEPTION: + case BAD_NEWS: + return Formatting.RED; + case INSTRUCTION: + case NEUTRAL: + return Formatting.GRAY; + case CUSTOM: + return Formatting.WHITE; + case INFORMATIVE: + return Formatting.AQUA; + case RESULT: + default: + case GOOD_NEWS: + return Formatting.GREEN; + case WARNING: + return Formatting.YELLOW; + + case DESCRIPTION: + return Formatting.GREEN; + case SYNTAX: + return Formatting.AQUA; + case HIGHLIGHT: + return Formatting.RED; + case SUBCOMMAND: + return Formatting.GRAY; + case NUMBER: + return Formatting.YELLOW; + } + } + + @Override + public String getMessagePrefixForType(EMessageType type) { + return ""; + } + + protected String createMessage(EMessageType type, String message) { + if (message == null || message.isEmpty()) return null; + return getMessagePrefixForType(type) + getChatFormatForType(type) + message; + } + + @Override + public String createMessage(ExecutionContext context, EMessageType type, String message) { + return createMessage(type, message); + } + + @Override + public String createMessage(CommandSender sender, EMessageType type, String message) { + return createMessage(type, message); + } + + @Override + public String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { + return helpPages.getHelpPage(sender, context, address, page); + } + + @Override + public String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { + return helpPages.getSyntax(sender, context, address); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java index 232d5cf..cebdb45 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/ChatHandlers.java @@ -1,20 +1,20 @@ -package io.dico.dicore.command.chat; - -/** - * Static factory methods for {@link IChatHandler} - */ -public class ChatHandlers { - private static final IChatHandler defaultChat; - - private ChatHandlers() { - - } - - public static IChatHandler defaultChat() { - return defaultChat; - } - - static { - defaultChat = new AbstractChatHandler(); - } -} +package io.dico.dicore.command.chat; + +/** + * Static factory methods for {@link IChatHandler} + */ +public class ChatHandlers { + private static final IChatHandler defaultChat; + + private ChatHandlers() { + + } + + public static IChatHandler defaultChat() { + return defaultChat; + } + + static { + defaultChat = new AbstractChatHandler(); + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java index 98283ef..0dd6002 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/IChatHandler.java @@ -1,60 +1,60 @@ -package io.dico.dicore.command.chat; - -import io.dico.dicore.Formatting; -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.command.CommandSender; - -//TODO add methods to send JSON messages -public interface IChatHandler { - - default void sendMessage(ExecutionContext context, EMessageType type, String message) { - message = createMessage(context, type, message); - if (message != null) { - context.getSender().sendMessage(message); - } - } - - default void sendMessage(CommandSender sender, EMessageType type, String message) { - message = createMessage(sender, type, message); - if (message != null) { - sender.sendMessage(message); - } - } - - default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) { - sendMessage(context, EMessageType.EXCEPTION, exception.getMessage()); - } - - default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) { - if (exception instanceof CommandException) { - handleCommandException(sender, context, (CommandException) exception); - } else { - sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command"); - exception.printStackTrace(); - } - } - - default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { - sender.sendMessage(createHelpMessage(sender, context, address, page)); - } - - default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { - sender.sendMessage(createSyntaxMessage(sender, context, address)); - } - - Formatting getChatFormatForType(EMessageType type); - - String getMessagePrefixForType(EMessageType type); - - String createMessage(ExecutionContext context, EMessageType type, String message); - - String createMessage(CommandSender sender, EMessageType type, String message); - - String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page); - - String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address); - -} +package io.dico.dicore.command.chat; + +import io.dico.dicore.Formatting; +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import org.bukkit.command.CommandSender; + +//TODO add methods to send JSON messages +public interface IChatHandler { + + default void sendMessage(ExecutionContext context, EMessageType type, String message) { + message = createMessage(context, type, message); + if (message != null) { + context.getSender().sendMessage(message); + } + } + + default void sendMessage(CommandSender sender, EMessageType type, String message) { + message = createMessage(sender, type, message); + if (message != null) { + sender.sendMessage(message); + } + } + + default void handleCommandException(CommandSender sender, ExecutionContext context, CommandException exception) { + sendMessage(context, EMessageType.EXCEPTION, exception.getMessage()); + } + + default void handleException(CommandSender sender, ExecutionContext context, Throwable exception) { + if (exception instanceof CommandException) { + handleCommandException(sender, context, (CommandException) exception); + } else { + sendMessage(sender, EMessageType.EXCEPTION, "An internal error occurred whilst executing this command"); + exception.printStackTrace(); + } + } + + default void sendHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page) { + sender.sendMessage(createHelpMessage(sender, context, address, page)); + } + + default void sendSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address) { + sender.sendMessage(createSyntaxMessage(sender, context, address)); + } + + Formatting getChatFormatForType(EMessageType type); + + String getMessagePrefixForType(EMessageType type); + + String createMessage(ExecutionContext context, EMessageType type, String message); + + String createMessage(CommandSender sender, EMessageType type, String message); + + String createHelpMessage(CommandSender sender, ExecutionContext context, ICommandAddress address, int page); + + String createSyntaxMessage(CommandSender sender, ExecutionContext context, ICommandAddress address); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java index 4382d93..616e85a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpPages.java @@ -1,92 +1,92 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.defaults.*; -import org.bukkit.permissions.Permissible; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class HelpPages { - private @NotNull IPageBuilder pageBuilder; - private @NotNull IPageLayout pageLayout; - private int pageLength; - private @NotNull List helpTopics; - private @NotNull IHelpTopic syntaxTopic; - - public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List helpTopics) { - this.pageBuilder = pageBuilder; - this.pageLayout = pageLayout; - this.pageLength = pageLength; - this.syntaxTopic = syntaxTopic; - this.helpTopics = helpTopics; - } - - public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) { - this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics))); - } - - @SuppressWarnings("RedundantArrayCreation") - public static HelpPages newDefaultHelpPages() { - IHelpTopic syntaxTopic = new SyntaxHelpTopic(); - return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12, - syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()}); - } - - public @NotNull IPageBuilder getPageBuilder() { - return pageBuilder; - } - - public void setPageBuilder(@NotNull IPageBuilder pageBuilder) { - this.pageBuilder = pageBuilder; - } - - public @NotNull IPageLayout getPageLayout() { - return pageLayout; - } - - public void setPageLayout(@NotNull IPageLayout pageLayout) { - this.pageLayout = pageLayout; - } - - public int getPageLength() { - return pageLength; - } - - public void setPageLength(int pageLength) { - this.pageLength = pageLength; - } - - public @NotNull IHelpTopic getSyntaxTopic() { - return syntaxTopic; - } - - public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) { - this.syntaxTopic = syntaxTopic; - } - - @NotNull - public List getHelpTopics() { - return helpTopics; - } - - public void setHelpTopics(@NotNull List helpTopics) { - this.helpTopics = helpTopics; - } - - public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) { - return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength); - } - - public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) { - List components = syntaxTopic.getComponents(address, viewer, context, false); - if (components.isEmpty()) { - return getHelpPage(viewer, context, address, 1); - } - return DefaultPageBuilder.combine(components); - } - -} +package io.dico.dicore.command.chat.help; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.defaults.*; +import org.bukkit.permissions.Permissible; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class HelpPages { + private @NotNull IPageBuilder pageBuilder; + private @NotNull IPageLayout pageLayout; + private int pageLength; + private @NotNull List helpTopics; + private @NotNull IHelpTopic syntaxTopic; + + public HelpPages(@NotNull IPageBuilder pageBuilder, @NotNull IPageLayout pageLayout, int pageLength, @NotNull IHelpTopic syntaxTopic, @NotNull List helpTopics) { + this.pageBuilder = pageBuilder; + this.pageLayout = pageLayout; + this.pageLength = pageLength; + this.syntaxTopic = syntaxTopic; + this.helpTopics = helpTopics; + } + + public HelpPages(IPageBuilder pageBuilder, IPageLayout pageLayout, int pageLength, IHelpTopic syntaxTopic, IHelpTopic... helpTopics) { + this(pageBuilder, pageLayout, pageLength, syntaxTopic, new ArrayList<>(Arrays.asList(helpTopics))); + } + + @SuppressWarnings("RedundantArrayCreation") + public static HelpPages newDefaultHelpPages() { + IHelpTopic syntaxTopic = new SyntaxHelpTopic(); + return new HelpPages(new DefaultPageBuilder(), new DefaultPageLayout(), 12, + syntaxTopic, new IHelpTopic[]{new DescriptionHelpTopic(), syntaxTopic, new SubcommandsHelpTopic()}); + } + + public @NotNull IPageBuilder getPageBuilder() { + return pageBuilder; + } + + public void setPageBuilder(@NotNull IPageBuilder pageBuilder) { + this.pageBuilder = pageBuilder; + } + + public @NotNull IPageLayout getPageLayout() { + return pageLayout; + } + + public void setPageLayout(@NotNull IPageLayout pageLayout) { + this.pageLayout = pageLayout; + } + + public int getPageLength() { + return pageLength; + } + + public void setPageLength(int pageLength) { + this.pageLength = pageLength; + } + + public @NotNull IHelpTopic getSyntaxTopic() { + return syntaxTopic; + } + + public void setSyntaxTopic(@NotNull IHelpTopic syntaxTopic) { + this.syntaxTopic = syntaxTopic; + } + + @NotNull + public List getHelpTopics() { + return helpTopics; + } + + public void setHelpTopics(@NotNull List helpTopics) { + this.helpTopics = helpTopics; + } + + public @NotNull String getHelpPage(Permissible viewer, ExecutionContext context, ICommandAddress address, int page) { + return pageBuilder.getPage(helpTopics, pageLayout, address, viewer, context, page, pageLength); + } + + public @NotNull String getSyntax(Permissible viewer, ExecutionContext context, ICommandAddress address) { + List components = syntaxTopic.getComponents(address, viewer, context, false); + if (components.isEmpty()) { + return getHelpPage(viewer, context, address, 1); + } + return DefaultPageBuilder.combine(components); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java index 7aeb304..b295a35 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/HelpTopicModifier.java @@ -1,24 +1,24 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; -import java.util.Objects; - -public abstract class HelpTopicModifier implements IHelpTopic { - private final IHelpTopic delegate; - - public HelpTopicModifier(IHelpTopic delegate) { - this.delegate = Objects.requireNonNull(delegate); - } - - @Override - public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context); - } - - protected abstract List modify(List components, ICommandAddress target, Permissible viewer, ExecutionContext context); - -} +package io.dico.dicore.command.chat.help; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import org.bukkit.permissions.Permissible; + +import java.util.List; +import java.util.Objects; + +public abstract class HelpTopicModifier implements IHelpTopic { + private final IHelpTopic delegate; + + public HelpTopicModifier(IHelpTopic delegate) { + this.delegate = Objects.requireNonNull(delegate); + } + + @Override + public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { + return modify(delegate.getComponents(target, viewer, context, true), target, viewer, context); + } + + protected abstract List modify(List components, ICommandAddress target, Permissible viewer, ExecutionContext context); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java index e1867b5..446c7d7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpComponent.java @@ -1,9 +1,9 @@ -package io.dico.dicore.command.chat.help; - -public interface IHelpComponent { - - int lineCount(); - - void appendTo(StringBuilder sb); - -} +package io.dico.dicore.command.chat.help; + +public interface IHelpComponent { + + int lineCount(); + + void appendTo(StringBuilder sb); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java index 0b3fba2..0f1b5c9 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IHelpTopic.java @@ -1,23 +1,23 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IHelpTopic { - - /** - * Get the components of this help topic - * - * @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 context Context of the command execution - * @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 - */ - List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage); - - -} +package io.dico.dicore.command.chat.help; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import org.bukkit.permissions.Permissible; + +import java.util.List; + +public interface IHelpTopic { + + /** + * Get the components of this help topic + * + * @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 context Context of the command execution + * @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 + */ + List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage); + + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java index 1ae3561..2789975 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBorder.java @@ -1,7 +1,7 @@ -package io.dico.dicore.command.chat.help; - -public interface IPageBorder extends IHelpComponent { - - void setPageCount(int pageCount); - -} +package io.dico.dicore.command.chat.help; + +public interface IPageBorder extends IHelpComponent { + + void setPageCount(int pageCount); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java index 86d9450..0584ade 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageBuilder.java @@ -1,13 +1,13 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IPageBuilder { - - String getPage(List helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen); - -} +package io.dico.dicore.command.chat.help; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import org.bukkit.permissions.Permissible; + +import java.util.List; + +public interface IPageBuilder { + + String getPage(List helpTopics, IPageLayout pageLayout, ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum, int pageLen); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java index 3223e32..32c41d7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/IPageLayout.java @@ -1,20 +1,20 @@ -package io.dico.dicore.command.chat.help; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import org.bukkit.permissions.Permissible; - -public interface IPageLayout { - - /** - * Get the page borders for a help page - * - * @param target the address that help is displayed for - * @param viewer the viewer of the help page, or null if irrelevant - * @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) - * @return the page borders. - */ - PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum); - -} +package io.dico.dicore.command.chat.help; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import org.bukkit.permissions.Permissible; + +public interface IPageLayout { + + /** + * Get the page borders for a help page + * + * @param target the address that help is displayed for + * @param viewer the viewer of the help page, or null if irrelevant + * @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) + * @return the page borders. + */ + PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java index 43c0514..cd105b1 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/PageBorders.java @@ -1,76 +1,76 @@ -package io.dico.dicore.command.chat.help; - -import java.util.Arrays; - -public class PageBorders { - private final IPageBorder header, footer; - - public PageBorders(IPageBorder header, IPageBorder footer) { - this.header = header; - this.footer = footer; - } - - public IPageBorder getHeader() { - return header; - } - - public IPageBorder getFooter() { - return footer; - } - - public static IPageBorder simpleBorder(String... lines) { - return new SimplePageBorder(lines); - } - - public static IPageBorder disappearingBorder(int pageNum, String... lines) { - return disappearingBorder(pageNum, 0, lines); - } - - public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) { - return new DisappearingPageBorder(pageNum, keptLines, lines); - } - - static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder { - private final String replacedSequence; - - public SimplePageBorder(String replacedSequence, String... lines) { - super(lines); - this.replacedSequence = replacedSequence; - } - - public SimplePageBorder(String... lines) { - super(lines); - this.replacedSequence = "%pageCount%"; - } - - @Override - public void setPageCount(int pageCount) { - String[] lines = this.lines; - for (int i = 0; i < lines.length; i++) { - lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount)); - } - } - - } - - static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder { - private final int pageNum; - private final int keptLines; - - public DisappearingPageBorder(int pageNum, int keptLines, String... lines) { - super(lines); - this.pageNum = pageNum; - this.keptLines = keptLines; - } - - @Override - public void setPageCount(int pageCount) { - if (pageCount == pageNum) { - String[] lines = this.lines; - this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length); - } - } - - } - -} +package io.dico.dicore.command.chat.help; + +import java.util.Arrays; + +public class PageBorders { + private final IPageBorder header, footer; + + public PageBorders(IPageBorder header, IPageBorder footer) { + this.header = header; + this.footer = footer; + } + + public IPageBorder getHeader() { + return header; + } + + public IPageBorder getFooter() { + return footer; + } + + public static IPageBorder simpleBorder(String... lines) { + return new SimplePageBorder(lines); + } + + public static IPageBorder disappearingBorder(int pageNum, String... lines) { + return disappearingBorder(pageNum, 0, lines); + } + + public static IPageBorder disappearingBorder(int pageNum, int keptLines, String... lines) { + return new DisappearingPageBorder(pageNum, keptLines, lines); + } + + static class SimplePageBorder extends SimpleHelpComponent implements IPageBorder { + private final String replacedSequence; + + public SimplePageBorder(String replacedSequence, String... lines) { + super(lines); + this.replacedSequence = replacedSequence; + } + + public SimplePageBorder(String... lines) { + super(lines); + this.replacedSequence = "%pageCount%"; + } + + @Override + public void setPageCount(int pageCount) { + String[] lines = this.lines; + for (int i = 0; i < lines.length; i++) { + lines[i] = lines[i].replace(replacedSequence, Integer.toString(pageCount)); + } + } + + } + + static class DisappearingPageBorder extends SimpleHelpComponent implements IPageBorder { + private final int pageNum; + private final int keptLines; + + public DisappearingPageBorder(int pageNum, int keptLines, String... lines) { + super(lines); + this.pageNum = pageNum; + this.keptLines = keptLines; + } + + @Override + public void setPageCount(int pageCount) { + if (pageCount == pageNum) { + String[] lines = this.lines; + this.lines = Arrays.copyOfRange(lines, Math.max(0, lines.length - keptLines), lines.length); + } + } + + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java index 22707fd..1b74d0f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/SimpleHelpComponent.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.chat.help; - -public class SimpleHelpComponent implements IHelpComponent { - String[] lines; - - public SimpleHelpComponent(String... lines) { - this.lines = lines; - } - - @Override - public int lineCount() { - return lines.length; - } - - @Override - public void appendTo(StringBuilder sb) { - String[] lines = this.lines; - int len = lines.length; - if (0 < len) { - sb.append(lines[0]); - } - for (int i = 1; i < len; i++) { - sb.append('\n').append(lines[i]); - } - } - -} +package io.dico.dicore.command.chat.help; + +public class SimpleHelpComponent implements IHelpComponent { + String[] lines; + + public SimpleHelpComponent(String... lines) { + this.lines = lines; + } + + @Override + public int lineCount() { + return lines.length; + } + + @Override + public void appendTo(StringBuilder sb) { + String[] lines = this.lines; + int len = lines.length; + if (0 < len) { + sb.append(lines[0]); + } + for (int i = 1; i < len; i++) { + sb.append('\n').append(lines[i]); + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java index a584e7e..e06730f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageBuilder.java @@ -1,115 +1,115 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.*; -import org.bukkit.permissions.Permissible; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; - -public class DefaultPageBuilder implements IPageBuilder { - - @Override - public String getPage(List helpTopics, IPageLayout pageLayout, ICommandAddress target, - Permissible viewer, ExecutionContext context, int pageNum, int pageLen) { - if (pageLen <= 0 || pageNum < 0) { - throw new IllegalArgumentException(); - } - - List components = new LinkedList<>(); - for (IHelpTopic topic : helpTopics) { - components.addAll(topic.getComponents(target, viewer, context, true)); - } - - PageBorders pageBorders = null; - int componentStartIdx = -1; - int componentEndIdx = -1; - int totalPageCount = 0; - int curPageLines = 0; - - ListIterator iterator = components.listIterator(); - - while (iterator.hasNext()) { - if (curPageLines == 0) { - - if (pageBorders != null) { - iterator.add(pageBorders.getFooter()); - } - - if (pageNum == totalPageCount) { - componentStartIdx = iterator.nextIndex(); - } else if (pageNum + 1 == totalPageCount) { - componentEndIdx = iterator.nextIndex(); - } - - pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1); - - if (pageBorders != null) { - iterator.add(pageBorders.getHeader()); - iterator.previous(); - - curPageLines += pageBorders.getFooter().lineCount(); - } - - totalPageCount++; - } - - IHelpComponent component = iterator.next(); - int lineCount = component.lineCount(); - curPageLines += lineCount; - - if (curPageLines >= pageLen) { - curPageLines = 0; - } - } - - if (componentStartIdx == -1) { - // page does not exist - return ""; - } - - if (componentEndIdx == -1) { - componentEndIdx = components.size(); - } - - StringBuilder sb = new StringBuilder(); - iterator = components.listIterator(componentStartIdx); - int count = componentEndIdx - componentStartIdx; - boolean first = true; - - while (count-- > 0) { - IHelpComponent component = iterator.next(); - if (component instanceof IPageBorder) { - ((IPageBorder) component).setPageCount(totalPageCount); - } - if (first) { - first = false; - } else { - sb.append('\n'); - } - component.appendTo(sb); - - } - - return sb.toString(); - } - - public static String combine(List components) { - StringBuilder rv = new StringBuilder(); - - Iterator iterator = components.iterator(); - if (iterator.hasNext()) { - iterator.next().appendTo(rv); - } - while (iterator.hasNext()) { - rv.append('\n'); - iterator.next().appendTo(rv); - } - - return rv.toString(); - } - -} +package io.dico.dicore.command.chat.help.defaults; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.*; +import org.bukkit.permissions.Permissible; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +public class DefaultPageBuilder implements IPageBuilder { + + @Override + public String getPage(List helpTopics, IPageLayout pageLayout, ICommandAddress target, + Permissible viewer, ExecutionContext context, int pageNum, int pageLen) { + if (pageLen <= 0 || pageNum < 0) { + throw new IllegalArgumentException(); + } + + List components = new LinkedList<>(); + for (IHelpTopic topic : helpTopics) { + components.addAll(topic.getComponents(target, viewer, context, true)); + } + + PageBorders pageBorders = null; + int componentStartIdx = -1; + int componentEndIdx = -1; + int totalPageCount = 0; + int curPageLines = 0; + + ListIterator iterator = components.listIterator(); + + while (iterator.hasNext()) { + if (curPageLines == 0) { + + if (pageBorders != null) { + iterator.add(pageBorders.getFooter()); + } + + if (pageNum == totalPageCount) { + componentStartIdx = iterator.nextIndex(); + } else if (pageNum + 1 == totalPageCount) { + componentEndIdx = iterator.nextIndex(); + } + + pageBorders = pageLayout.getPageBorders(target, viewer, context, totalPageCount + 1); + + if (pageBorders != null) { + iterator.add(pageBorders.getHeader()); + iterator.previous(); + + curPageLines += pageBorders.getFooter().lineCount(); + } + + totalPageCount++; + } + + IHelpComponent component = iterator.next(); + int lineCount = component.lineCount(); + curPageLines += lineCount; + + if (curPageLines >= pageLen) { + curPageLines = 0; + } + } + + if (componentStartIdx == -1) { + // page does not exist + return ""; + } + + if (componentEndIdx == -1) { + componentEndIdx = components.size(); + } + + StringBuilder sb = new StringBuilder(); + iterator = components.listIterator(componentStartIdx); + int count = componentEndIdx - componentStartIdx; + boolean first = true; + + while (count-- > 0) { + IHelpComponent component = iterator.next(); + if (component instanceof IPageBorder) { + ((IPageBorder) component).setPageCount(totalPageCount); + } + if (first) { + first = false; + } else { + sb.append('\n'); + } + component.appendTo(sb); + + } + + return sb.toString(); + } + + public static String combine(List components) { + StringBuilder rv = new StringBuilder(); + + Iterator iterator = components.iterator(); + if (iterator.hasNext()) { + iterator.next().appendTo(rv); + } + while (iterator.hasNext()) { + rv.append('\n'); + iterator.next().appendTo(rv); + } + + return rv.toString(); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java index 8d3d004..d950b09 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DefaultPageLayout.java @@ -1,40 +1,40 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.ModifiableCommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.IChatHandler; -import io.dico.dicore.command.chat.help.IPageBorder; -import io.dico.dicore.command.chat.help.IPageLayout; -import io.dico.dicore.command.chat.help.PageBorders; -import org.bukkit.permissions.Permissible; - -public class DefaultPageLayout implements IPageLayout { - - @Override - public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) { - IChatHandler c = context.getAddress().getChatHandler(); - String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE); - Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE); - Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL); - - String nextPageCommand; - ICommandAddress executor = context.getAddress(); - if (((ModifiableCommandAddress) executor).hasHelpCommand()) { - nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1); - } else { - nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1); - } - - String header = prefix + informative + "Help page " + number + pageNum + informative + - '/' + number + "%pageCount%" + informative + " for /" + target.getAddress(); - String footer = informative + "Type /" + nextPageCommand + " for the next page"; - - IPageBorder headerBorder = PageBorders.simpleBorder("", header); - IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer); - return new PageBorders(headerBorder, footerBorder); - } - -} +package io.dico.dicore.command.chat.help.defaults; + +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.ModifiableCommandAddress; +import io.dico.dicore.Formatting; +import io.dico.dicore.command.chat.IChatHandler; +import io.dico.dicore.command.chat.help.IPageBorder; +import io.dico.dicore.command.chat.help.IPageLayout; +import io.dico.dicore.command.chat.help.PageBorders; +import org.bukkit.permissions.Permissible; + +public class DefaultPageLayout implements IPageLayout { + + @Override + public PageBorders getPageBorders(ICommandAddress target, Permissible viewer, ExecutionContext context, int pageNum) { + IChatHandler c = context.getAddress().getChatHandler(); + String prefix = c.getMessagePrefixForType(EMessageType.INFORMATIVE); + Formatting informative = c.getChatFormatForType(EMessageType.INFORMATIVE); + Formatting number = c.getChatFormatForType(EMessageType.NEUTRAL); + + String nextPageCommand; + ICommandAddress executor = context.getAddress(); + if (((ModifiableCommandAddress) executor).hasHelpCommand()) { + nextPageCommand = ((ModifiableCommandAddress) executor).getHelpCommand().getAddress() + ' ' + (pageNum + 1); + } else { + nextPageCommand = executor.getAddress() + ' ' + (pageNum + 1); + } + + String header = prefix + informative + "Help page " + number + pageNum + informative + + '/' + number + "%pageCount%" + informative + " for /" + target.getAddress(); + String footer = informative + "Type /" + nextPageCommand + " for the next page"; + + IPageBorder headerBorder = PageBorders.simpleBorder("", header); + IPageBorder footerBorder = PageBorders.disappearingBorder(pageNum, footer); + return new PageBorders(headerBorder, footerBorder); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java index ae88ea2..0899db4 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/DescriptionHelpTopic.java @@ -1,45 +1,45 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.Command; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.ArrayList; -import java.util.List; - -public class DescriptionHelpTopic implements IHelpTopic { - - @Override - public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - List out = new ArrayList<>(); - Formatting format = context.getFormat(EMessageType.DESCRIPTION); - - if (!target.hasCommand()) { - return out; - } - Command command = target.getCommand(); - String[] description = command.getDescription(); - if (description.length == 0) { - String shortDescription = command.getShortDescription(); - if (shortDescription == null) { - return out; - } - - description = new String[]{shortDescription}; - } - - for (int i = 0; i < description.length; i++) { - description[i] = format + description[i]; - } - - out.add(new SimpleHelpComponent(description)); - return out; - } - -} +package io.dico.dicore.command.chat.help.defaults; + +import io.dico.dicore.command.Command; +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.Formatting; +import io.dico.dicore.command.chat.help.IHelpComponent; +import io.dico.dicore.command.chat.help.IHelpTopic; +import io.dico.dicore.command.chat.help.SimpleHelpComponent; +import org.bukkit.permissions.Permissible; + +import java.util.ArrayList; +import java.util.List; + +public class DescriptionHelpTopic implements IHelpTopic { + + @Override + public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { + List out = new ArrayList<>(); + Formatting format = context.getFormat(EMessageType.DESCRIPTION); + + if (!target.hasCommand()) { + return out; + } + Command command = target.getCommand(); + String[] description = command.getDescription(); + if (description.length == 0) { + String shortDescription = command.getShortDescription(); + if (shortDescription == null) { + return out; + } + + description = new String[]{shortDescription}; + } + + for (int i = 0; i < description.length; i++) { + description[i] = format + description[i]; + } + + out.add(new SimpleHelpComponent(description)); + return out; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java index 0e680ae..b302769 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SubcommandsHelpTopic.java @@ -1,59 +1,59 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import org.bukkit.command.CommandSender; -import org.bukkit.permissions.Permissible; - -import java.util.*; - -public class SubcommandsHelpTopic implements IHelpTopic { - - @Override - public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - Collection mainKeys = target.getChildrenMainKeys(); - if (mainKeys.isEmpty()) { - return Collections.emptyList(); - } - - List result = new ArrayList<>(); - - mainKeys = new ArrayList<>(target.getChildrenMainKeys()); - ((ArrayList) mainKeys).sort(null); - - CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender(); - for (String key : mainKeys) { - ICommandAddress child = target.getChild(key); - if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) { - result.add(getComponent(child, viewer, context)); - } - } - - return result; - } - - public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) { - Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND); - Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT); - - String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey(); - - String description = child.hasCommand() ? child.getCommand().getShortDescription() : null; - if (description != null) { - Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION); - return new SimpleHelpComponent(address, descriptionFormat + description); - } - - return new SimpleHelpComponent(address); - } - - private static Formatting colorOf(ExecutionContext context, EMessageType type) { - return context.getAddress().getChatHandler().getChatFormatForType(type); - } - -} +package io.dico.dicore.command.chat.help.defaults; + +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.Formatting; +import io.dico.dicore.command.chat.help.IHelpComponent; +import io.dico.dicore.command.chat.help.IHelpTopic; +import io.dico.dicore.command.chat.help.SimpleHelpComponent; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permissible; + +import java.util.*; + +public class SubcommandsHelpTopic implements IHelpTopic { + + @Override + public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { + Collection mainKeys = target.getChildrenMainKeys(); + if (mainKeys.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + + mainKeys = new ArrayList<>(target.getChildrenMainKeys()); + ((ArrayList) mainKeys).sort(null); + + CommandSender sender = viewer instanceof CommandSender ? (CommandSender) viewer : context.getSender(); + for (String key : mainKeys) { + ICommandAddress child = target.getChild(key); + if ((child.hasChildren() || child.hasUserDeclaredCommand()) && child.getCommand().isVisibleTo(sender)) { + result.add(getComponent(child, viewer, context)); + } + } + + return result; + } + + public IHelpComponent getComponent(ICommandAddress child, Permissible viewer, ExecutionContext context) { + Formatting subcommand = colorOf(context, EMessageType.SUBCOMMAND); + Formatting highlight = colorOf(context, EMessageType.HIGHLIGHT); + + String address = subcommand + "/" + child.getParent().getAddress() + ' ' + highlight + child.getMainKey(); + + String description = child.hasCommand() ? child.getCommand().getShortDescription() : null; + if (description != null) { + Formatting descriptionFormat = colorOf(context, EMessageType.DESCRIPTION); + return new SimpleHelpComponent(address, descriptionFormat + description); + } + + return new SimpleHelpComponent(address); + } + + private static Formatting colorOf(ExecutionContext context, EMessageType type) { + return context.getAddress().getChatHandler().getChatFormatForType(type); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java index d0e3ebe..f0f820f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/defaults/SyntaxHelpTopic.java @@ -1,92 +1,92 @@ -package io.dico.dicore.command.chat.help.defaults; - -import io.dico.dicore.command.Command; -import io.dico.dicore.command.EMessageType; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.Formatting; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import io.dico.dicore.command.chat.help.SimpleHelpComponent; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import org.bukkit.permissions.Permissible; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class SyntaxHelpTopic implements IHelpTopic { - - @Override - public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - if (!target.hasCommand()) { - return Collections.emptyList(); - } - - if (target.hasChildren()) { - if (!isForPage) { - // HelpPages will send help instead of syntax, which might in turn include syntax as well. - return Collections.emptyList(); - } - - if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) { - // no point adding syntax at all - return Collections.emptyList(); - } - } - - StringBuilder line = new StringBuilder(); - if (isForPage) - line.append(context.getFormat(EMessageType.SYNTAX)) - .append("Syntax: "); - - line.append('/') - .append(context.getFormat(EMessageType.INSTRUCTION)) - .append(target.getAddress()) - .append(' '); - - addShortSyntax(line, target, context); - - return Collections.singletonList(new SimpleHelpComponent(line.toString())); - } - - private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) { - if (address.hasCommand()) { - Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX); - Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT); - builder.append(syntaxColor); - - Command command = address.getCommand(); - ParameterList list = command.getParameterList(); - Parameter repeated = list.getRepeatedParameter(); - - int requiredCount = list.getRequiredCount(); - List> indexedParameters = list.getIndexedParameters(); - for (int i = 0, n = indexedParameters.size(); i < n; i++) { - builder.append(i < requiredCount ? " <" : " ["); - Parameter param = indexedParameters.get(i); - builder.append(param.getName()); - if (param == repeated) { - builder.append(highlight).append("...").append(syntaxColor); - } - builder.append(i < requiredCount ? '>' : ']'); - } - - Map> parametersByName = list.getParametersByName(); - for (Parameter param : parametersByName.values()) { - if (param.isFlag()) { - builder.append(" [").append(param.getName()); - if (param.expectsInput()) { - builder.append(" <").append(param.getName()).append(">"); - } - builder.append(']'); - } - } - - } else { - builder.append(' '); - } - } - -} +package io.dico.dicore.command.chat.help.defaults; + +import io.dico.dicore.command.Command; +import io.dico.dicore.command.EMessageType; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.Formatting; +import io.dico.dicore.command.chat.help.IHelpComponent; +import io.dico.dicore.command.chat.help.IHelpTopic; +import io.dico.dicore.command.chat.help.SimpleHelpComponent; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.ParameterList; +import org.bukkit.permissions.Permissible; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class SyntaxHelpTopic implements IHelpTopic { + + @Override + public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { + if (!target.hasCommand()) { + return Collections.emptyList(); + } + + if (target.hasChildren()) { + if (!isForPage) { + // HelpPages will send help instead of syntax, which might in turn include syntax as well. + return Collections.emptyList(); + } + + if (!target.hasUserDeclaredCommand() && !target.getCommand().getParameterList().hasAnyParameters()) { + // no point adding syntax at all + return Collections.emptyList(); + } + } + + StringBuilder line = new StringBuilder(); + if (isForPage) + line.append(context.getFormat(EMessageType.SYNTAX)) + .append("Syntax: "); + + line.append('/') + .append(context.getFormat(EMessageType.INSTRUCTION)) + .append(target.getAddress()) + .append(' '); + + addShortSyntax(line, target, context); + + return Collections.singletonList(new SimpleHelpComponent(line.toString())); + } + + private static void addShortSyntax(StringBuilder builder, ICommandAddress address, ExecutionContext ctx) { + if (address.hasCommand()) { + Formatting syntaxColor = ctx.getFormat(EMessageType.SYNTAX); + Formatting highlight = ctx.getFormat(EMessageType.HIGHLIGHT); + builder.append(syntaxColor); + + Command command = address.getCommand(); + ParameterList list = command.getParameterList(); + Parameter repeated = list.getRepeatedParameter(); + + int requiredCount = list.getRequiredCount(); + List> indexedParameters = list.getIndexedParameters(); + for (int i = 0, n = indexedParameters.size(); i < n; i++) { + builder.append(i < requiredCount ? " <" : " ["); + Parameter param = indexedParameters.get(i); + builder.append(param.getName()); + if (param == repeated) { + builder.append(highlight).append("...").append(syntaxColor); + } + builder.append(i < requiredCount ? '>' : ']'); + } + + Map> parametersByName = list.getParametersByName(); + for (Parameter param : parametersByName.values()) { + if (param.isFlag()) { + builder.append(" [").append(param.getName()); + if (param.expectsInput()) { + builder.append(" <").append(param.getName()).append(">"); + } + builder.append(']'); + } + } + + } else { + builder.append(' '); + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java index 4f0026d..eb3605d 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/EInsertionStage.java @@ -1,29 +1,29 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public enum EInsertionStage implements IInsertionFunction { - START { - @Override - public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return 0; - } - }, - CENTER { - @Override - public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return current.size() / 2; - } - }, - END { - @Override - public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return current.size(); - } - } -} +package io.dico.dicore.command.chat.help.insertion; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.IHelpComponent; +import org.bukkit.permissions.Permissible; + +import java.util.List; + +public enum EInsertionStage implements IInsertionFunction { + START { + @Override + public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { + return 0; + } + }, + CENTER { + @Override + public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { + return current.size() / 2; + } + }, + END { + @Override + public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { + return current.size(); + } + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java index f153165..75b52d0 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/HelpComponentInserter.java @@ -1,43 +1,43 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.HelpTopicModifier; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import org.bukkit.permissions.Permissible; - -import java.util.ArrayList; -import java.util.List; - -public class HelpComponentInserter extends HelpTopicModifier { - private List insertions = new ArrayList<>(); - - public HelpComponentInserter(IHelpTopic delegate) { - super(delegate); - } - - @Override - protected List modify(List components, ICommandAddress target, Permissible viewer, ExecutionContext context) { - // int componentCount = components.size(); - - for (int i = insertions.size() - 1; i >= 0; i--) { - IInsertion insertion = insertions.get(i); - int idx = insertion.insertionIndex(components, target, viewer, context); - List inserted = insertion.getComponents(target, viewer, context, true); - components.addAll(idx, inserted); - } - - return components; - } - - public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) { - return insert(Insertions.combine(helpTopic, insertionFunction)); - } - - public HelpComponentInserter insert(IInsertion insertion) { - insertions.add(insertion); - return this; - } - -} +package io.dico.dicore.command.chat.help.insertion; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.HelpTopicModifier; +import io.dico.dicore.command.chat.help.IHelpComponent; +import io.dico.dicore.command.chat.help.IHelpTopic; +import org.bukkit.permissions.Permissible; + +import java.util.ArrayList; +import java.util.List; + +public class HelpComponentInserter extends HelpTopicModifier { + private List insertions = new ArrayList<>(); + + public HelpComponentInserter(IHelpTopic delegate) { + super(delegate); + } + + @Override + protected List modify(List components, ICommandAddress target, Permissible viewer, ExecutionContext context) { + // int componentCount = components.size(); + + for (int i = insertions.size() - 1; i >= 0; i--) { + IInsertion insertion = insertions.get(i); + int idx = insertion.insertionIndex(components, target, viewer, context); + List inserted = insertion.getComponents(target, viewer, context, true); + components.addAll(idx, inserted); + } + + return components; + } + + public HelpComponentInserter insert(IInsertionFunction insertionFunction, IHelpTopic helpTopic) { + return insert(Insertions.combine(helpTopic, insertionFunction)); + } + + public HelpComponentInserter insert(IInsertion insertion) { + insertions.add(insertion); + return this; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java index 757cb91..92309d3 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertion.java @@ -1,7 +1,7 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.chat.help.IHelpTopic; - -interface IInsertion extends IHelpTopic, IInsertionFunction { - -} +package io.dico.dicore.command.chat.help.insertion; + +import io.dico.dicore.command.chat.help.IHelpTopic; + +interface IInsertion extends IHelpTopic, IInsertionFunction { + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java index e99c246..356d002 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/IInsertionFunction.java @@ -1,14 +1,14 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public interface IInsertionFunction { - - int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context); - -} +package io.dico.dicore.command.chat.help.insertion; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.IHelpComponent; +import org.bukkit.permissions.Permissible; + +import java.util.List; + +public interface IInsertionFunction { + + int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java index 39b2784..220f05b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/chat/help/insertion/Insertions.java @@ -1,31 +1,31 @@ -package io.dico.dicore.command.chat.help.insertion; - -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.chat.help.IHelpComponent; -import io.dico.dicore.command.chat.help.IHelpTopic; -import org.bukkit.permissions.Permissible; - -import java.util.List; - -public class Insertions { - - private Insertions() { - - } - - public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) { - return new IInsertion() { - @Override - public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { - return topic.getComponents(target, viewer, context, true); - } - - @Override - public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { - return function.insertionIndex(current, target, viewer, context); - } - }; - } - -} +package io.dico.dicore.command.chat.help.insertion; + +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.chat.help.IHelpComponent; +import io.dico.dicore.command.chat.help.IHelpTopic; +import org.bukkit.permissions.Permissible; + +import java.util.List; + +public class Insertions { + + private Insertions() { + + } + + public static IInsertion combine(IHelpTopic topic, IInsertionFunction function) { + return new IInsertion() { + @Override + public List getComponents(ICommandAddress target, Permissible viewer, ExecutionContext context, boolean isForPage) { + return topic.getComponents(target, viewer, context, true); + } + + @Override + public int insertionIndex(List current, ICommandAddress target, Permissible viewer, ExecutionContext context) { + return function.insertionIndex(current, target, viewer, context); + } + }; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java index aa69730..5646814 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentBuffer.java @@ -1,295 +1,295 @@ -package io.dico.dicore.command.parameter; - -import io.dico.dicore.command.CommandException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * Buffer for the arguments. - * Easy to traverse for the parser. - */ -public class ArgumentBuffer extends AbstractList implements Iterator, RandomAccess { - private String[] array; - private int cursor = 0; // index of the next return value - private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy() - - public ArgumentBuffer(String label, String[] args) { - this(combine(label, args)); - } - - private static String[] combine(String label, String[] args) { - String[] result; - //if (args.length > 0 && "".equals(args[args.length - 1])) { - // // drop the last element of args if it is empty - // result = args; - //} else { - result = new String[args.length + 1]; - //} - System.arraycopy(args, 0, result, 1, result.length - 1); - result[0] = Objects.requireNonNull(label); - return result; - } - - /** - * Constructs a new ArgumentBuffer using the given array, without copying it first. - * None of the array its elements should be empty. - * - * @param array the array - * @throws NullPointerException if the array or any of its elements are null - */ - public ArgumentBuffer(String[] array) { - for (String elem : array) { - if (elem == null) throw new NullPointerException("ArgumentBuffer array element"); - } - this.array = array; - - } - - public int getCursor() { - return cursor; - } - - public @NotNull ArgumentBuffer setCursor(int cursor) { - if (cursor <= 0) { - cursor = 0; - } else if (size() <= cursor) { - cursor = size(); - } - this.cursor = cursor; - return this; - } - - @Override - public int size() { - return array.length; - } - - @Override - public @NotNull String get(int index) { - return array[index]; - } - - public int nextIndex() { - return cursor; - } - - public int previousIndex() { - return cursor - 1; - } - - public int remainingElements() { - return size() - nextIndex() - 1; - } - - @Override - public boolean hasNext() { - return nextIndex() < size(); - } - - public boolean hasPrevious() { - return 0 <= previousIndex(); - } - - /** - * Unlike conventional ListIterator implementations, this returns null if there is no next element - * - * @return the next value, or null - */ - @Override - public @Nullable String next() { - return hasNext() ? get(cursor++) : null; - } - - public @NotNull String requireNext(String parameterName) throws CommandException { - String next = next(); - if (next == null) { - throw CommandException.missingArgument(parameterName); - } - return next; - } - - // useful for completion code - public @NotNull String nextOrEmpty() { - return hasNext() ? get(cursor++) : ""; - } - - /** - * Unlike conventional ListIterator implementations, this returns null if there is no previous element - * - * @return the previous value, or null - */ - public @Nullable String previous() { - return hasPrevious() ? get(--cursor) : null; - } - - public @Nullable String peekNext() { - return hasNext() ? get(cursor) : null; - } - - public @Nullable String peekPrevious() { - return hasPrevious() ? get(cursor - 1) : null; - } - - public @NotNull ArgumentBuffer advance() { - return advance(1); - } - - public @NotNull ArgumentBuffer advance(int amount) { - cursor = Math.min(Math.max(0, cursor + amount), size()); - return this; - } - - public @NotNull ArgumentBuffer rewind() { - return rewind(1); - } - - public @NotNull ArgumentBuffer rewind(int amount) { - return advance(-amount); - } - - @NotNull String[] getArray() { - return array; - } - - public @NotNull String[] getArrayFromCursor() { - return getArrayFromIndex(cursor); - } - - public @NotNull String[] getArrayFromIndex(int index) { - return Arrays.copyOfRange(array, index, array.length); - } - - public @NotNull String getRawInput() { - return String.join(" ", array); - } - - public @NotNull String[] toArray() { - return array.clone(); - } - - @Override - public @NotNull Iterator iterator() { - return this; - } - - @Override - public @NotNull ListIterator listIterator() { - return new ListIterator() { - @Override - public boolean hasNext() { - return ArgumentBuffer.this.hasNext(); - } - - @Override - public String next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return ArgumentBuffer.this.next(); - } - - @Override - public boolean hasPrevious() { - return ArgumentBuffer.this.hasPrevious(); - } - - @Override - public String previous() { - if (!hasPrevious()) { - throw new NoSuchElementException(); - } - return ArgumentBuffer.this.previous(); - } - - @Override - public int nextIndex() { - return ArgumentBuffer.this.nextIndex(); - } - - @Override - public int previousIndex() { - return ArgumentBuffer.this.previousIndex(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public void set(String s) { - throw new UnsupportedOperationException(); - } - - @Override - public void add(String s) { - throw new UnsupportedOperationException(); - } - }; - } - - public void dropTrailingEmptyElements() { - int removeCount = 0; - String[] array = this.array; - for (int i = array.length - 1; i >= 0; i--) { - if ("".equals(array[i])) { - removeCount++; - } - } - - if (removeCount > 0) { - String[] newArray = new String[array.length - removeCount]; - System.arraycopy(array, 0, newArray, 0, newArray.length); - this.array = newArray; - - if (cursor > newArray.length) { - cursor = newArray.length; - } - } - } - - /** - * Preprocess this argument buffer with the given preprocessor - * - * @param preProcessor preprocessor - * @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed. - */ - public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) { - return preProcessor.process(this, -1); - } - - /** - * 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 - * 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. - * - * @return A view of this buffer that doesn't affect this buffer's cursor. - */ - public ArgumentBuffer getUnaffectingCopy() { - // 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. - - ArgumentBuffer unaffectingCopy = this.unaffectingCopy; - if (unaffectingCopy == null) { - this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array); - } - unaffectingCopy.cursor = this.cursor; - return unaffectingCopy; - } - - @SuppressWarnings("MethodDoesntCallSuperMethod") - public @NotNull ArgumentBuffer clone() { - ArgumentBuffer result = getUnaffectingCopy(); - this.unaffectingCopy = null; - return result; - } - - @Override - public String toString() { - return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor()); - } - -} +package io.dico.dicore.command.parameter; + +import io.dico.dicore.command.CommandException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Buffer for the arguments. + * Easy to traverse for the parser. + */ +public class ArgumentBuffer extends AbstractList implements Iterator, RandomAccess { + private String[] array; + private int cursor = 0; // index of the next return value + private transient ArgumentBuffer unaffectingCopy = null; // see #getUnaffectingCopy() + + public ArgumentBuffer(String label, String[] args) { + this(combine(label, args)); + } + + private static String[] combine(String label, String[] args) { + String[] result; + //if (args.length > 0 && "".equals(args[args.length - 1])) { + // // drop the last element of args if it is empty + // result = args; + //} else { + result = new String[args.length + 1]; + //} + System.arraycopy(args, 0, result, 1, result.length - 1); + result[0] = Objects.requireNonNull(label); + return result; + } + + /** + * Constructs a new ArgumentBuffer using the given array, without copying it first. + * None of the array its elements should be empty. + * + * @param array the array + * @throws NullPointerException if the array or any of its elements are null + */ + public ArgumentBuffer(String[] array) { + for (String elem : array) { + if (elem == null) throw new NullPointerException("ArgumentBuffer array element"); + } + this.array = array; + + } + + public int getCursor() { + return cursor; + } + + public @NotNull ArgumentBuffer setCursor(int cursor) { + if (cursor <= 0) { + cursor = 0; + } else if (size() <= cursor) { + cursor = size(); + } + this.cursor = cursor; + return this; + } + + @Override + public int size() { + return array.length; + } + + @Override + public @NotNull String get(int index) { + return array[index]; + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public int remainingElements() { + return size() - nextIndex() - 1; + } + + @Override + public boolean hasNext() { + return nextIndex() < size(); + } + + public boolean hasPrevious() { + return 0 <= previousIndex(); + } + + /** + * Unlike conventional ListIterator implementations, this returns null if there is no next element + * + * @return the next value, or null + */ + @Override + public @Nullable String next() { + return hasNext() ? get(cursor++) : null; + } + + public @NotNull String requireNext(String parameterName) throws CommandException { + String next = next(); + if (next == null) { + throw CommandException.missingArgument(parameterName); + } + return next; + } + + // useful for completion code + public @NotNull String nextOrEmpty() { + return hasNext() ? get(cursor++) : ""; + } + + /** + * Unlike conventional ListIterator implementations, this returns null if there is no previous element + * + * @return the previous value, or null + */ + public @Nullable String previous() { + return hasPrevious() ? get(--cursor) : null; + } + + public @Nullable String peekNext() { + return hasNext() ? get(cursor) : null; + } + + public @Nullable String peekPrevious() { + return hasPrevious() ? get(cursor - 1) : null; + } + + public @NotNull ArgumentBuffer advance() { + return advance(1); + } + + public @NotNull ArgumentBuffer advance(int amount) { + cursor = Math.min(Math.max(0, cursor + amount), size()); + return this; + } + + public @NotNull ArgumentBuffer rewind() { + return rewind(1); + } + + public @NotNull ArgumentBuffer rewind(int amount) { + return advance(-amount); + } + + @NotNull String[] getArray() { + return array; + } + + public @NotNull String[] getArrayFromCursor() { + return getArrayFromIndex(cursor); + } + + public @NotNull String[] getArrayFromIndex(int index) { + return Arrays.copyOfRange(array, index, array.length); + } + + public @NotNull String getRawInput() { + return String.join(" ", array); + } + + public @NotNull String[] toArray() { + return array.clone(); + } + + @Override + public @NotNull Iterator iterator() { + return this; + } + + @Override + public @NotNull ListIterator listIterator() { + return new ListIterator() { + @Override + public boolean hasNext() { + return ArgumentBuffer.this.hasNext(); + } + + @Override + public String next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return ArgumentBuffer.this.next(); + } + + @Override + public boolean hasPrevious() { + return ArgumentBuffer.this.hasPrevious(); + } + + @Override + public String previous() { + if (!hasPrevious()) { + throw new NoSuchElementException(); + } + return ArgumentBuffer.this.previous(); + } + + @Override + public int nextIndex() { + return ArgumentBuffer.this.nextIndex(); + } + + @Override + public int previousIndex() { + return ArgumentBuffer.this.previousIndex(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + @Override + public void set(String s) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(String s) { + throw new UnsupportedOperationException(); + } + }; + } + + public void dropTrailingEmptyElements() { + int removeCount = 0; + String[] array = this.array; + for (int i = array.length - 1; i >= 0; i--) { + if ("".equals(array[i])) { + removeCount++; + } + } + + if (removeCount > 0) { + String[] newArray = new String[array.length - removeCount]; + System.arraycopy(array, 0, newArray, 0, newArray.length); + this.array = newArray; + + if (cursor > newArray.length) { + cursor = newArray.length; + } + } + } + + /** + * Preprocess this argument buffer with the given preprocessor + * + * @param preProcessor preprocessor + * @return a new ArgumentBuffer with processed contents. Might be this buffer if nothing changed. + */ + public @NotNull ArgumentBuffer preprocessArguments(IArgumentPreProcessor preProcessor) { + return preProcessor.process(this, -1); + } + + /** + * 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 + * 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. + * + * @return A view of this buffer that doesn't affect this buffer's cursor. + */ + public ArgumentBuffer getUnaffectingCopy() { + // 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. + + ArgumentBuffer unaffectingCopy = this.unaffectingCopy; + if (unaffectingCopy == null) { + this.unaffectingCopy = unaffectingCopy = new ArgumentBuffer(array); + } + unaffectingCopy.cursor = this.cursor; + return unaffectingCopy; + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + public @NotNull ArgumentBuffer clone() { + ArgumentBuffer result = getUnaffectingCopy(); + this.unaffectingCopy = null; + return result; + } + + @Override + public String toString() { + return String.format("ArgumentBuffer(size = %d, cursor = %d)", size(), getCursor()); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java index ce818b7..a9ccd20 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ArgumentMergingPreProcessor.java @@ -1,177 +1,177 @@ -package io.dico.dicore.command.parameter; - -public class ArgumentMergingPreProcessor implements IArgumentPreProcessor { - private final String tokens; - private final char escapeChar; - - public ArgumentMergingPreProcessor(String tokens, char escapeChar) { - if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException(); - this.tokens = tokens; - this.escapeChar = escapeChar; - } - - @Override - public ArgumentBuffer process(ArgumentBuffer buffer, int count) { - Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count); - String[] array = parser.doProcess(); - ArgumentBuffer result = new ArgumentBuffer(array); - parser.updateBuffer(result); - return result; - } - - private class Parser { - private final String[] args; - private final int start; - private final int count; - - private int foundSectionCount; - private int currentIndex; - private int sectionStart; - private char closingToken; - private int sectionEnd; - private int removeCount; - - Parser(String[] args, int start, int count) { - this.start = start; - this.args = args; - this.count = count; - } - - private void reset() { - foundSectionCount = 0; - currentIndex = start; - sectionStart = -1; - closingToken = 0; - sectionEnd = -1; - removeCount = 0; - } - - private boolean findNextSectionStart() { - if (count >= 0 && foundSectionCount >= count) return false; - - while (currentIndex < args.length) { - String arg = args[currentIndex]; - if (arg == null) { - throw new IllegalArgumentException(); - } - - if (arg.isEmpty()) { - ++currentIndex; - continue; - } - - int openingTokenIndex = tokens.indexOf(arg.charAt(0)); - if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) { - ++currentIndex; - continue; - } - - // found - closingToken = tokens.charAt(openingTokenIndex | 1); - sectionStart = currentIndex; - return true; - } - - return false; - } - - private boolean findNextSectionEnd() { - while (currentIndex < args.length) { - String arg = args[currentIndex]; - if (arg == null) { - throw new IllegalArgumentException(); - } - - if (arg.isEmpty() - || arg.charAt(arg.length() - 1) != closingToken - || (sectionStart == currentIndex && arg.length() == 1)) { - ++currentIndex; - continue; - } - - if (escapeChar != 0 - && arg.length() > 1 - && arg.charAt(arg.length() - 2) == escapeChar) { - // escaped - ++currentIndex; - continue; - } - - // found - closingToken = 0; - sectionEnd = currentIndex; - ++currentIndex; - return true; - } - - return false; - } - - private void processFoundSection() { - if (sectionStart == sectionEnd) { - String arg = args[sectionStart]; - args[sectionStart] = arg.substring(1, arg.length() - 1); - return; - } - - removeCount += sectionEnd - sectionStart; - - StringBuilder sb = new StringBuilder(); - sb.append(args[sectionStart].substring(1)); - - for (int i = sectionStart + 1; i < sectionEnd; i++) { - sb.append(' '); - sb.append(args[i]); - args[i] = null; - } - sb.append(' '); - sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1)); - args[sectionEnd] = null; - - args[sectionStart] = sb.toString(); - - sectionStart = -1; - sectionEnd = -1; - - ++foundSectionCount; - } - - String[] doProcess() { - reset(); - - while (findNextSectionStart()) { - if (findNextSectionEnd()) { - processFoundSection(); - } else { - currentIndex = sectionStart + 1; - } - } - - if (removeCount == 0) { - return args; - } - - String[] result = new String[args.length - removeCount]; - int i = 0; - for (String arg : args) { - if (arg != null) { - result[i++] = arg; - } - } - - return result; - } - - void updateBuffer(ArgumentBuffer buffer) { - if (count < 0) { - buffer.setCursor(start); - } else { - buffer.setCursor(currentIndex); - } - } - - } - -} - - +package io.dico.dicore.command.parameter; + +public class ArgumentMergingPreProcessor implements IArgumentPreProcessor { + private final String tokens; + private final char escapeChar; + + public ArgumentMergingPreProcessor(String tokens, char escapeChar) { + if ((tokens.length() & 1) != 0 || tokens.isEmpty()) throw new IllegalArgumentException(); + this.tokens = tokens; + this.escapeChar = escapeChar; + } + + @Override + public ArgumentBuffer process(ArgumentBuffer buffer, int count) { + Parser parser = new Parser(buffer.getArray().clone(), buffer.getCursor(), count); + String[] array = parser.doProcess(); + ArgumentBuffer result = new ArgumentBuffer(array); + parser.updateBuffer(result); + return result; + } + + private class Parser { + private final String[] args; + private final int start; + private final int count; + + private int foundSectionCount; + private int currentIndex; + private int sectionStart; + private char closingToken; + private int sectionEnd; + private int removeCount; + + Parser(String[] args, int start, int count) { + this.start = start; + this.args = args; + this.count = count; + } + + private void reset() { + foundSectionCount = 0; + currentIndex = start; + sectionStart = -1; + closingToken = 0; + sectionEnd = -1; + removeCount = 0; + } + + private boolean findNextSectionStart() { + if (count >= 0 && foundSectionCount >= count) return false; + + while (currentIndex < args.length) { + String arg = args[currentIndex]; + if (arg == null) { + throw new IllegalArgumentException(); + } + + if (arg.isEmpty()) { + ++currentIndex; + continue; + } + + int openingTokenIndex = tokens.indexOf(arg.charAt(0)); + if (openingTokenIndex == -1 || (openingTokenIndex & 1) != 0) { + ++currentIndex; + continue; + } + + // found + closingToken = tokens.charAt(openingTokenIndex | 1); + sectionStart = currentIndex; + return true; + } + + return false; + } + + private boolean findNextSectionEnd() { + while (currentIndex < args.length) { + String arg = args[currentIndex]; + if (arg == null) { + throw new IllegalArgumentException(); + } + + if (arg.isEmpty() + || arg.charAt(arg.length() - 1) != closingToken + || (sectionStart == currentIndex && arg.length() == 1)) { + ++currentIndex; + continue; + } + + if (escapeChar != 0 + && arg.length() > 1 + && arg.charAt(arg.length() - 2) == escapeChar) { + // escaped + ++currentIndex; + continue; + } + + // found + closingToken = 0; + sectionEnd = currentIndex; + ++currentIndex; + return true; + } + + return false; + } + + private void processFoundSection() { + if (sectionStart == sectionEnd) { + String arg = args[sectionStart]; + args[sectionStart] = arg.substring(1, arg.length() - 1); + return; + } + + removeCount += sectionEnd - sectionStart; + + StringBuilder sb = new StringBuilder(); + sb.append(args[sectionStart].substring(1)); + + for (int i = sectionStart + 1; i < sectionEnd; i++) { + sb.append(' '); + sb.append(args[i]); + args[i] = null; + } + sb.append(' '); + sb.append(args[sectionEnd].substring(0, args[sectionEnd].length() - 1)); + args[sectionEnd] = null; + + args[sectionStart] = sb.toString(); + + sectionStart = -1; + sectionEnd = -1; + + ++foundSectionCount; + } + + String[] doProcess() { + reset(); + + while (findNextSectionStart()) { + if (findNextSectionEnd()) { + processFoundSection(); + } else { + currentIndex = sectionStart + 1; + } + } + + if (removeCount == 0) { + return args; + } + + String[] result = new String[args.length - removeCount]; + int i = 0; + for (String arg : args) { + if (arg != null) { + result[i++] = arg; + } + } + + return result; + } + + void updateBuffer(ArgumentBuffer buffer) { + if (count < 0) { + buffer.setCursor(start); + } else { + buffer.setCursor(currentIndex); + } + } + + } + +} + + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java index a5afce5..f486f52 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ContextParser.java @@ -1,276 +1,276 @@ -package io.dico.dicore.command.parameter; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.ExecutionContext; - -import java.lang.reflect.Array; -import java.util.*; - -public class ContextParser { - private final ExecutionContext m_context; - private final ArgumentBuffer m_buffer; - private final ParameterList m_paramList; - private final Parameter m_repeatedParam; - private final List> m_indexedParams; - private final int m_maxIndex; - private final int m_maxRequiredIndex; - - private Map m_valueMap; - private Set m_parsedKeys; - private int m_completionCursor = -1; - private Parameter m_completionTarget = null; - - public ContextParser(ExecutionContext context, - ParameterList parameterList, - Map valueMap, - Set keySet) { - m_context = context; - m_paramList = parameterList; - m_valueMap = valueMap; - m_parsedKeys = keySet; - - m_buffer = context.getBuffer(); - m_repeatedParam = m_paramList.getRepeatedParameter(); - m_indexedParams = m_paramList.getIndexedParameters(); - m_maxIndex = m_indexedParams.size() - 1; - m_maxRequiredIndex = m_paramList.getRequiredCount() - 1; - } - - public ExecutionContext getContext() { - return m_context; - } - - public Map getValueMap() { - return m_valueMap; - } - - public Set getParsedKeys() { - return m_parsedKeys; - } - - public void parse() throws CommandException { - parseAllParameters(); - } - - public int getCompletionCursor() { - if (!m_done) { - throw new IllegalStateException(); - } - return m_completionCursor; - } - - public Parameter getCompletionTarget() { - if (!m_done) { - throw new IllegalStateException(); - } - return m_completionTarget; - } - - // ################################ - // # PARSING METHODS # - // ################################ - - private boolean m_repeating = false; - private boolean m_done = false; - private int m_curParamIndex = -1; - private Parameter m_curParam = null; - private List m_curRepeatingList = null; - - private void parseAllParameters() throws CommandException { - try { - do { - prepareStateToParseParam(); - if (m_done) break; - parseCurParam(); - } while (!m_done); - - } finally { - m_curParam = null; - m_curRepeatingList = null; - assignDefaultValuesToUncomputedParams(); - arrayifyRepeatedParamValue(); - } - } - - private void prepareStateToParseParam() throws CommandException { - - boolean requireInput; - if (identifyFlag()) { - m_buffer.advance(); - prepareRepeatedParameterIfSet(); - requireInput = false; - - } else if (m_repeating) { - m_curParam = m_repeatedParam; - requireInput = false; - - } else if (m_curParamIndex < m_maxIndex) { - m_curParamIndex++; - m_curParam = m_indexedParams.get(m_curParamIndex); - prepareRepeatedParameterIfSet(); - requireInput = m_curParamIndex <= m_maxRequiredIndex; - - } else if (m_buffer.hasNext()) { - throw new CommandException("Too many arguments for /" + m_context.getAddress().getAddress()); - - } else { - m_done = true; - return; - } - - if (!m_buffer.hasNext()) { - if (requireInput) { - reportParameterRequired(m_curParam); - } - - if (m_repeating) { - m_done = true; - } - } - - } - - private boolean identifyFlag() { - String potentialFlag = m_buffer.peekNext(); - Parameter target; - if (potentialFlag != null - && potentialFlag.startsWith("-") - && (target = m_paramList.getParameterByName(potentialFlag)) != null - && target.isFlag() - && !m_valueMap.containsKey(potentialFlag) - -// Disabled because it's checked by {@link Parameter#parse(ExecutionContext, ArgumentBuffer)} -// && (target.getFlagPermission() == null || m_context.getSender().hasPermission(target.getFlagPermission())) - ) { - m_curParam = target; - return true; - } - - return false; - } - - private void prepareRepeatedParameterIfSet() throws CommandException { - if (m_curParam != null && m_curParam == m_repeatedParam) { - - if (m_curParam.isFlag() && m_curParamIndex < m_maxRequiredIndex) { - Parameter requiredParam = m_indexedParams.get(m_curParamIndex + 1); - reportParameterRequired(requiredParam); - } - - m_curRepeatingList = new ArrayList<>(); - assignValue(m_curRepeatingList); - m_repeating = true; - } - } - - private void reportParameterRequired(Parameter param) throws CommandException { - throw new CommandException("The argument '" + param.getName() + "' is required"); - } - - private void parseCurParam() throws CommandException { - if (!m_buffer.hasNext() && !m_curParam.isFlag()) { - assignDefaultValue(); - return; - } - - int cursorStart = m_buffer.getCursor(); - - if (m_context.isTabComplete() && "".equals(m_buffer.peekNext())) { - assignAsCompletionTarget(cursorStart); - return; - } - - Object parseResult; - try { - parseResult = m_curParam.parse(m_context, m_buffer); - } catch (CommandException e) { - assignAsCompletionTarget(cursorStart); - throw e; - } - - assignValue(parseResult); - m_parsedKeys.add(m_curParam.getName()); - } - - private void assignDefaultValue() throws CommandException { - assignValue(m_curParam.getDefaultValue(m_context, m_buffer)); - } - - private void assignAsCompletionTarget(int cursor) { - m_completionCursor = cursor; - m_completionTarget = m_curParam; - m_done = true; - } - - private void assignValue(Object value) { - if (m_repeating) { - m_curRepeatingList.add(value); - } else { - m_valueMap.put(m_curParam.getName(), value); - } - } - - private void assignDefaultValuesToUncomputedParams() throws CommandException { - // add default values for unset parameters - for (Map.Entry> entry : m_paramList.getParametersByName().entrySet()) { - String name = entry.getKey(); - if (!m_valueMap.containsKey(name)) { - if (m_repeatedParam == entry.getValue()) { - // below value will be turned into an array later - m_valueMap.put(name, Collections.emptyList()); - } else { - m_valueMap.put(name, entry.getValue().getDefaultValue(m_context, m_buffer)); - } - } - } - } - - private void arrayifyRepeatedParamValue() { - if (m_repeatedParam != null) { - m_valueMap.computeIfPresent(m_repeatedParam.getName(), (k, v) -> { - List list = (List) v; - Class returnType = m_repeatedParam.getType().getReturnType(); - Object array = Array.newInstance(returnType, list.size()); - ArraySetter setter = ArraySetter.getSetter(returnType); - for (int i = 0, n = list.size(); i < n; i++) { - setter.set(array, i, list.get(i)); - } - - return array; - }); - } - } - - private interface ArraySetter { - void set(Object array, int index, Object value); - - static ArraySetter getSetter(Class clazz) { - if (!clazz.isPrimitive()) { - return (array, index, value) -> ((Object[]) array)[index] = value; - } - - switch (clazz.getSimpleName()) { - case "boolean": - return (array, index, value) -> ((boolean[]) array)[index] = (boolean) value; - case "int": - return (array, index, value) -> ((int[]) array)[index] = (int) value; - case "double": - return (array, index, value) -> ((double[]) array)[index] = (double) value; - case "long": - return (array, index, value) -> ((long[]) array)[index] = (long) value; - case "short": - return (array, index, value) -> ((short[]) array)[index] = (short) value; - case "byte": - return (array, index, value) -> ((byte[]) array)[index] = (byte) value; - case "float": - return (array, index, value) -> ((float[]) array)[index] = (float) value; - case "char": - return (array, index, value) -> ((char[]) array)[index] = (char) value; - case "void": - default: - throw new InternalError("This should not happen"); - } - } - } - -} +package io.dico.dicore.command.parameter; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.ExecutionContext; + +import java.lang.reflect.Array; +import java.util.*; + +public class ContextParser { + private final ExecutionContext m_context; + private final ArgumentBuffer m_buffer; + private final ParameterList m_paramList; + private final Parameter m_repeatedParam; + private final List> m_indexedParams; + private final int m_maxIndex; + private final int m_maxRequiredIndex; + + private Map m_valueMap; + private Set m_parsedKeys; + private int m_completionCursor = -1; + private Parameter m_completionTarget = null; + + public ContextParser(ExecutionContext context, + ParameterList parameterList, + Map valueMap, + Set keySet) { + m_context = context; + m_paramList = parameterList; + m_valueMap = valueMap; + m_parsedKeys = keySet; + + m_buffer = context.getBuffer(); + m_repeatedParam = m_paramList.getRepeatedParameter(); + m_indexedParams = m_paramList.getIndexedParameters(); + m_maxIndex = m_indexedParams.size() - 1; + m_maxRequiredIndex = m_paramList.getRequiredCount() - 1; + } + + public ExecutionContext getContext() { + return m_context; + } + + public Map getValueMap() { + return m_valueMap; + } + + public Set getParsedKeys() { + return m_parsedKeys; + } + + public void parse() throws CommandException { + parseAllParameters(); + } + + public int getCompletionCursor() { + if (!m_done) { + throw new IllegalStateException(); + } + return m_completionCursor; + } + + public Parameter getCompletionTarget() { + if (!m_done) { + throw new IllegalStateException(); + } + return m_completionTarget; + } + + // ################################ + // # PARSING METHODS # + // ################################ + + private boolean m_repeating = false; + private boolean m_done = false; + private int m_curParamIndex = -1; + private Parameter m_curParam = null; + private List m_curRepeatingList = null; + + private void parseAllParameters() throws CommandException { + try { + do { + prepareStateToParseParam(); + if (m_done) break; + parseCurParam(); + } while (!m_done); + + } finally { + m_curParam = null; + m_curRepeatingList = null; + assignDefaultValuesToUncomputedParams(); + arrayifyRepeatedParamValue(); + } + } + + private void prepareStateToParseParam() throws CommandException { + + boolean requireInput; + if (identifyFlag()) { + m_buffer.advance(); + prepareRepeatedParameterIfSet(); + requireInput = false; + + } else if (m_repeating) { + m_curParam = m_repeatedParam; + requireInput = false; + + } else if (m_curParamIndex < m_maxIndex) { + m_curParamIndex++; + m_curParam = m_indexedParams.get(m_curParamIndex); + prepareRepeatedParameterIfSet(); + requireInput = m_curParamIndex <= m_maxRequiredIndex; + + } else if (m_buffer.hasNext()) { + throw new CommandException("Too many arguments for /" + m_context.getAddress().getAddress()); + + } else { + m_done = true; + return; + } + + if (!m_buffer.hasNext()) { + if (requireInput) { + reportParameterRequired(m_curParam); + } + + if (m_repeating) { + m_done = true; + } + } + + } + + private boolean identifyFlag() { + String potentialFlag = m_buffer.peekNext(); + Parameter target; + if (potentialFlag != null + && potentialFlag.startsWith("-") + && (target = m_paramList.getParameterByName(potentialFlag)) != null + && target.isFlag() + && !m_valueMap.containsKey(potentialFlag) + +// Disabled because it's checked by {@link Parameter#parse(ExecutionContext, ArgumentBuffer)} +// && (target.getFlagPermission() == null || m_context.getSender().hasPermission(target.getFlagPermission())) + ) { + m_curParam = target; + return true; + } + + return false; + } + + private void prepareRepeatedParameterIfSet() throws CommandException { + if (m_curParam != null && m_curParam == m_repeatedParam) { + + if (m_curParam.isFlag() && m_curParamIndex < m_maxRequiredIndex) { + Parameter requiredParam = m_indexedParams.get(m_curParamIndex + 1); + reportParameterRequired(requiredParam); + } + + m_curRepeatingList = new ArrayList<>(); + assignValue(m_curRepeatingList); + m_repeating = true; + } + } + + private void reportParameterRequired(Parameter param) throws CommandException { + throw new CommandException("The argument '" + param.getName() + "' is required"); + } + + private void parseCurParam() throws CommandException { + if (!m_buffer.hasNext() && !m_curParam.isFlag()) { + assignDefaultValue(); + return; + } + + int cursorStart = m_buffer.getCursor(); + + if (m_context.isTabComplete() && "".equals(m_buffer.peekNext())) { + assignAsCompletionTarget(cursorStart); + return; + } + + Object parseResult; + try { + parseResult = m_curParam.parse(m_context, m_buffer); + } catch (CommandException e) { + assignAsCompletionTarget(cursorStart); + throw e; + } + + assignValue(parseResult); + m_parsedKeys.add(m_curParam.getName()); + } + + private void assignDefaultValue() throws CommandException { + assignValue(m_curParam.getDefaultValue(m_context, m_buffer)); + } + + private void assignAsCompletionTarget(int cursor) { + m_completionCursor = cursor; + m_completionTarget = m_curParam; + m_done = true; + } + + private void assignValue(Object value) { + if (m_repeating) { + m_curRepeatingList.add(value); + } else { + m_valueMap.put(m_curParam.getName(), value); + } + } + + private void assignDefaultValuesToUncomputedParams() throws CommandException { + // add default values for unset parameters + for (Map.Entry> entry : m_paramList.getParametersByName().entrySet()) { + String name = entry.getKey(); + if (!m_valueMap.containsKey(name)) { + if (m_repeatedParam == entry.getValue()) { + // below value will be turned into an array later + m_valueMap.put(name, Collections.emptyList()); + } else { + m_valueMap.put(name, entry.getValue().getDefaultValue(m_context, m_buffer)); + } + } + } + } + + private void arrayifyRepeatedParamValue() { + if (m_repeatedParam != null) { + m_valueMap.computeIfPresent(m_repeatedParam.getName(), (k, v) -> { + List list = (List) v; + Class returnType = m_repeatedParam.getType().getReturnType(); + Object array = Array.newInstance(returnType, list.size()); + ArraySetter setter = ArraySetter.getSetter(returnType); + for (int i = 0, n = list.size(); i < n; i++) { + setter.set(array, i, list.get(i)); + } + + return array; + }); + } + } + + private interface ArraySetter { + void set(Object array, int index, Object value); + + static ArraySetter getSetter(Class clazz) { + if (!clazz.isPrimitive()) { + return (array, index, value) -> ((Object[]) array)[index] = value; + } + + switch (clazz.getSimpleName()) { + case "boolean": + return (array, index, value) -> ((boolean[]) array)[index] = (boolean) value; + case "int": + return (array, index, value) -> ((int[]) array)[index] = (int) value; + case "double": + return (array, index, value) -> ((double[]) array)[index] = (double) value; + case "long": + return (array, index, value) -> ((long[]) array)[index] = (long) value; + case "short": + return (array, index, value) -> ((short[]) array)[index] = (short) value; + case "byte": + return (array, index, value) -> ((byte[]) array)[index] = (byte) value; + case "float": + return (array, index, value) -> ((float[]) array)[index] = (float) value; + case "char": + return (array, index, value) -> ((char[]) array)[index] = (char) value; + case "void": + default: + throw new InternalError("This should not happen"); + } + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java index 0b8198e..5495cce 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/IArgumentPreProcessor.java @@ -1,41 +1,41 @@ -package io.dico.dicore.command.parameter; - -/** - * An interface to process tokens such as quotes - */ -@Deprecated -public interface IArgumentPreProcessor { - - /** - * Preprocess the arguments contained within the given ArgumentBuffer. - * If no changes are made, this might return the same buffer. - * Any arguments preceding {@code buffer.getCursor()} will not be affected. - * - *

- * 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. - *

- * - * @param buffer the argument buffer - * @param count the maximum number of (processed) arguments - * @return the arguments after preprocessing - */ - ArgumentBuffer process(ArgumentBuffer buffer, int count); - - IArgumentPreProcessor NONE = (buffer, count) -> buffer; - - /** - * 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. - * Example: []{}"" - * 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 - * @return The IArgumentPreProcessor - */ - static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) { - return new ArgumentMergingPreProcessor(tokens, escapeChar); - } - -} - +package io.dico.dicore.command.parameter; + +/** + * An interface to process tokens such as quotes + */ +@Deprecated +public interface IArgumentPreProcessor { + + /** + * Preprocess the arguments contained within the given ArgumentBuffer. + * If no changes are made, this might return the same buffer. + * Any arguments preceding {@code buffer.getCursor()} will not be affected. + * + *

+ * 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. + *

+ * + * @param buffer the argument buffer + * @param count the maximum number of (processed) arguments + * @return the arguments after preprocessing + */ + ArgumentBuffer process(ArgumentBuffer buffer, int count); + + IArgumentPreProcessor NONE = (buffer, count) -> buffer; + + /** + * 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. + * Example: []{}"" + * 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 + * @return The IArgumentPreProcessor + */ + static IArgumentPreProcessor mergeOnTokens(String tokens, char escapeChar) { + return new ArgumentMergingPreProcessor(tokens, escapeChar); + } + +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java index 613d057..f35628c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/ParameterList.java @@ -1,148 +1,148 @@ -package io.dico.dicore.command.parameter; - -import java.util.*; - -/** - * IParameter definition for a command - */ -@SuppressWarnings("UnusedReturnValue") -public class ParameterList { - //private ParameterList parent; - private List> indexedParameters; - private Map> byName; - //private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE; - private int requiredCount = -1; - private boolean repeatFinalParameter; - - // 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 - // indexed parameter. This might be a flag. As such, this field exists to ensure the correct - // parameter is taken for repeating - private boolean finalParameterMayBeFlag; - - /* - public ParameterList(ParameterList parent) { - this(); - if (parent.repeatFinalParameter) { - throw new IllegalArgumentException("Parent may not have repeating parameters"); - } - this.parent = parent; - }*/ - - public ParameterList() { - this.indexedParameters = new ArrayList<>(); - this.byName = new LinkedHashMap<>(); - this.repeatFinalParameter = false; - } - - /* - public IArgumentPreProcessor getArgumentPreProcessor() { - return argumentPreProcessor; - } - - public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) { - this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor; - return this; - }*/ - - public boolean repeatFinalParameter() { - return repeatFinalParameter; - } - - public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) { - this.repeatFinalParameter = repeatFinalParameter; - return this; - } - - public boolean finalParameterMayBeFlag() { - return finalParameterMayBeFlag; - } - - public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) { - this.finalParameterMayBeFlag = finalParameterMayBeFlag; - return this; - } - - public int getRequiredCount() { - return requiredCount == -1 ? indexedParameters.size() : requiredCount; - } - - public ParameterList setRequiredCount(int requiredCount) { - this.requiredCount = requiredCount; - return this; - } - - public boolean hasAnyParameters() { - return !byName.isEmpty(); - } - - public int getIndexedParameterCount() { - return indexedParameters.size(); - } - - public List> getIndexedParameters() { - return Collections.unmodifiableList(indexedParameters); - } - - public Parameter getParameterByName(String name) { - return byName.get(name); - } - - public String getIndexedParameterName(int index) { - return indexedParameters.get(index).getName(); - } - - public Map> getParametersByName() { - return Collections.unmodifiableMap(byName); - } - - /** - * Add the given parameter to the end of this parameter list - * Can be a flag - * - * @param parameter the parameter - * @return this - */ - public ParameterList addParameter(Parameter parameter) { - return addParameter(-1, parameter); - } - - /** - * Add the given parameter to this parameter list - * If the parameter is a flag, the index is ignored - * - * @param index parameter index number, -1 if end - * @param parameter the parameter - * @return this - * @throws NullPointerException if parameter is null - */ - public ParameterList addParameter(int index, Parameter parameter) { - //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag()); - byName.put(parameter.getName(), parameter); - if (!parameter.isFlag()) { - indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter); - } - return this; - } - - public Parameter getRepeatedParameter() { - if (!repeatFinalParameter) { - return null; - } - if (finalParameterMayBeFlag) { - Iterator> iterator = byName.values().iterator(); - Parameter result = null; - while (iterator.hasNext()) { - result = iterator.next(); - } - return result; - } - - if (indexedParameters.isEmpty()) { - return null; - } - - return indexedParameters.get(indexedParameters.size() - 1); - } - -} +package io.dico.dicore.command.parameter; + +import java.util.*; + +/** + * IParameter definition for a command + */ +@SuppressWarnings("UnusedReturnValue") +public class ParameterList { + //private ParameterList parent; + private List> indexedParameters; + private Map> byName; + //private IArgumentPreProcessor argumentPreProcessor = IArgumentPreProcessor.NONE; + private int requiredCount = -1; + private boolean repeatFinalParameter; + + // 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 + // indexed parameter. This might be a flag. As such, this field exists to ensure the correct + // parameter is taken for repeating + private boolean finalParameterMayBeFlag; + + /* + public ParameterList(ParameterList parent) { + this(); + if (parent.repeatFinalParameter) { + throw new IllegalArgumentException("Parent may not have repeating parameters"); + } + this.parent = parent; + }*/ + + public ParameterList() { + this.indexedParameters = new ArrayList<>(); + this.byName = new LinkedHashMap<>(); + this.repeatFinalParameter = false; + } + + /* + public IArgumentPreProcessor getArgumentPreProcessor() { + return argumentPreProcessor; + } + + public ParameterList setArgumentPreProcessor(IArgumentPreProcessor argumentPreProcessor) { + this.argumentPreProcessor = argumentPreProcessor == null ? IArgumentPreProcessor.NONE : argumentPreProcessor; + return this; + }*/ + + public boolean repeatFinalParameter() { + return repeatFinalParameter; + } + + public ParameterList setRepeatFinalParameter(boolean repeatFinalParameter) { + this.repeatFinalParameter = repeatFinalParameter; + return this; + } + + public boolean finalParameterMayBeFlag() { + return finalParameterMayBeFlag; + } + + public ParameterList setFinalParameterMayBeFlag(boolean finalParameterMayBeFlag) { + this.finalParameterMayBeFlag = finalParameterMayBeFlag; + return this; + } + + public int getRequiredCount() { + return requiredCount == -1 ? indexedParameters.size() : requiredCount; + } + + public ParameterList setRequiredCount(int requiredCount) { + this.requiredCount = requiredCount; + return this; + } + + public boolean hasAnyParameters() { + return !byName.isEmpty(); + } + + public int getIndexedParameterCount() { + return indexedParameters.size(); + } + + public List> getIndexedParameters() { + return Collections.unmodifiableList(indexedParameters); + } + + public Parameter getParameterByName(String name) { + return byName.get(name); + } + + public String getIndexedParameterName(int index) { + return indexedParameters.get(index).getName(); + } + + public Map> getParametersByName() { + return Collections.unmodifiableMap(byName); + } + + /** + * Add the given parameter to the end of this parameter list + * Can be a flag + * + * @param parameter the parameter + * @return this + */ + public ParameterList addParameter(Parameter parameter) { + return addParameter(-1, parameter); + } + + /** + * Add the given parameter to this parameter list + * If the parameter is a flag, the index is ignored + * + * @param index parameter index number, -1 if end + * @param parameter the parameter + * @return this + * @throws NullPointerException if parameter is null + */ + public ParameterList addParameter(int index, Parameter parameter) { + //System.out.println("Added parameter " + parameter.getName() + ", flag: " + parameter.isFlag()); + byName.put(parameter.getName(), parameter); + if (!parameter.isFlag()) { + indexedParameters.add(index == -1 ? indexedParameters.size() : index, parameter); + } + return this; + } + + public Parameter getRepeatedParameter() { + if (!repeatFinalParameter) { + return null; + } + if (finalParameterMayBeFlag) { + Iterator> iterator = byName.values().iterator(); + Parameter result = null; + while (iterator.hasNext()) { + result = iterator.next(); + } + return result; + } + + if (indexedParameters.isEmpty()) { + return null; + } + + return indexedParameters.get(indexedParameters.size() - 1); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java index c23e09b..063d381 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/EnumParameterType.java @@ -1,46 +1,46 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.ArrayList; -import java.util.List; - -public class EnumParameterType extends SimpleParameterType { - private final E[] universe; - - public EnumParameterType(Class returnType) { - super(returnType); - universe = returnType.getEnumConstants(); - if (universe == null) { - throw new IllegalArgumentException("returnType must be an enum"); - } - } - - @Override - protected E parse(Parameter parameter, CommandSender sender, String input) throws CommandException { - for (E constant : universe) { - if (constant.name().equalsIgnoreCase(input)) { - return constant; - } - } - - throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist"); - } - - @Override - public List complete(Parameter parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { - String input = buffer.next().toUpperCase(); - List result = new ArrayList<>(); - for (E constant : universe) { - if (constant.name().toUpperCase().startsWith(input.toUpperCase())) { - result.add(constant.name().toLowerCase()); - } - } - return result; - } - -} +package io.dico.dicore.command.parameter.type; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.Parameter; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.ArrayList; +import java.util.List; + +public class EnumParameterType extends SimpleParameterType { + private final E[] universe; + + public EnumParameterType(Class returnType) { + super(returnType); + universe = returnType.getEnumConstants(); + if (universe == null) { + throw new IllegalArgumentException("returnType must be an enum"); + } + } + + @Override + protected E parse(Parameter parameter, CommandSender sender, String input) throws CommandException { + for (E constant : universe) { + if (constant.name().equalsIgnoreCase(input)) { + return constant; + } + } + + throw CommandException.invalidArgument(parameter.getName(), "the enum value does not exist"); + } + + @Override + public List complete(Parameter parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { + String input = buffer.next().toUpperCase(); + List result = new ArrayList<>(); + for (E constant : universe) { + if (constant.name().toUpperCase().startsWith(input.toUpperCase())) { + result.add(constant.name().toLowerCase()); + } + } + return result; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java index 780ea0d..381b15c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/IParameterTypeSelector.java @@ -1,44 +1,44 @@ -package io.dico.dicore.command.parameter.type; - -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} - */ -public interface IParameterTypeSelector { - - ParameterType selectExact(ParameterKey key); - - // ParameterType selectExactOrSubclass(ParameterKey key); - - ParameterType selectAny(ParameterKey key); - - - default ParameterType selectExact(Class returnType) { - return selectExact(returnType, null); - } - - default ParameterType selectExact(Class returnType, Class annotationClass) { - return selectExact(new ParameterKey(returnType, annotationClass)); - } - - /* - default ParameterType selectExactOrSubclass(Class returnType) { - return selectExactOrSubclass(returnType, null); - } - - default ParameterType selectExactOrSubclass(Class returnType, Class annotationClass) { - return selectExactOrSubclass(new ParameterKey(returnType, annotationClass)); - } - */ - default ParameterType selectAny(Class returnType) { - return selectAny(returnType, null); - } - - default ParameterType selectAny(Class returnType, Class annotationClass) { - return selectAny(new ParameterKey(returnType, annotationClass)); - } - - void addType(boolean infolessAlias, ParameterType type); - -} +package io.dico.dicore.command.parameter.type; + +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} + */ +public interface IParameterTypeSelector { + + ParameterType selectExact(ParameterKey key); + + // ParameterType selectExactOrSubclass(ParameterKey key); + + ParameterType selectAny(ParameterKey key); + + + default ParameterType selectExact(Class returnType) { + return selectExact(returnType, null); + } + + default ParameterType selectExact(Class returnType, Class annotationClass) { + return selectExact(new ParameterKey(returnType, annotationClass)); + } + + /* + default ParameterType selectExactOrSubclass(Class returnType) { + return selectExactOrSubclass(returnType, null); + } + + default ParameterType selectExactOrSubclass(Class returnType, Class annotationClass) { + return selectExactOrSubclass(new ParameterKey(returnType, annotationClass)); + } + */ + default ParameterType selectAny(Class returnType) { + return selectAny(returnType, null); + } + + default ParameterType selectAny(Class returnType, Class annotationClass) { + return selectAny(new ParameterKey(returnType, annotationClass)); + } + + void addType(boolean infolessAlias, ParameterType type); + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java index d407f87..ef86eab 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/MapBasedParameterTypeSelector.java @@ -1,114 +1,114 @@ -package io.dico.dicore.command.parameter.type; - -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.Map; - -/** - * Map based implementation of {@link IParameterTypeSelector} - */ -public class MapBasedParameterTypeSelector implements IParameterTypeSelector { - static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false); - private final Map> parameterTypeMap; - private final boolean useDefault; - - public MapBasedParameterTypeSelector(boolean useDefault) { - this.parameterTypeMap = new HashMap<>(); - this.useDefault = useDefault; - } - - @Override - public ParameterType selectExact(ParameterKey key) { - ParameterType out = parameterTypeMap.get(key); - if (useDefault && out == null) { - out = defaultSelector.selectExact(key); - } - if (out == null && key.getReturnType().isEnum()) { - //noinspection unchecked - out = new EnumParameterType(key.getReturnType()); - addType(false, out); - } - return cast(out); - } - - @Override - public ParameterType selectAny(ParameterKey key) { - ParameterType exact = selectExact(key); - if (exact != null) { - return exact; - } - - if (key.getAnnotationClass() != null) { - exact = selectExact(new ParameterKey(key.getReturnType())); - if (exact != null) { - return exact; - } - } - - Class returnType = key.getReturnType(); - Class annotationClass = key.getAnnotationClass(); - - ParameterType out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false); - if (out == null && useDefault) { - out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false); - } - if (out == null) { - out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true); - } - if (out == null && useDefault) { - out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true); - } - return cast(out); - } - - private static ParameterType selectByReturnType(Map> map, Class returnType, - Class annotationClass, boolean allowSubclass) { - ParameterType out = null; - if (allowSubclass) { - for (ParameterType type : map.values()) { - if (returnType.isAssignableFrom(type.getReturnType())) { - if (annotationClass == type.getAnnotationClass()) { - out = type; - break; - } - if (out == null) { - out = type; - } - } - } - } else { - for (ParameterType type : map.values()) { - if (returnType == type.getReturnType()) { - if (annotationClass == type.getAnnotationClass()) { - out = type; - break; - } - if (out == null) { - out = type; - } - } - } - } - return out; - } - - private static T cast(Object o) { - //noinspection unchecked - return (T) o; - } - - @Override - public void addType(boolean infolessAlias, ParameterType type) { - parameterTypeMap.put(type.getTypeKey(), type); - - if (infolessAlias) { - parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type); - } - } - - static { - // registers default parameter types - ParameterTypes.clinit(); - } - -} +package io.dico.dicore.command.parameter.type; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.Map; + +/** + * Map based implementation of {@link IParameterTypeSelector} + */ +public class MapBasedParameterTypeSelector implements IParameterTypeSelector { + static final MapBasedParameterTypeSelector defaultSelector = new MapBasedParameterTypeSelector(false); + private final Map> parameterTypeMap; + private final boolean useDefault; + + public MapBasedParameterTypeSelector(boolean useDefault) { + this.parameterTypeMap = new HashMap<>(); + this.useDefault = useDefault; + } + + @Override + public ParameterType selectExact(ParameterKey key) { + ParameterType out = parameterTypeMap.get(key); + if (useDefault && out == null) { + out = defaultSelector.selectExact(key); + } + if (out == null && key.getReturnType().isEnum()) { + //noinspection unchecked + out = new EnumParameterType(key.getReturnType()); + addType(false, out); + } + return cast(out); + } + + @Override + public ParameterType selectAny(ParameterKey key) { + ParameterType exact = selectExact(key); + if (exact != null) { + return exact; + } + + if (key.getAnnotationClass() != null) { + exact = selectExact(new ParameterKey(key.getReturnType())); + if (exact != null) { + return exact; + } + } + + Class returnType = key.getReturnType(); + Class annotationClass = key.getAnnotationClass(); + + ParameterType out = selectByReturnType(parameterTypeMap, returnType, annotationClass, false); + if (out == null && useDefault) { + out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, false); + } + if (out == null) { + out = selectByReturnType(parameterTypeMap, returnType, annotationClass, true); + } + if (out == null && useDefault) { + out = selectByReturnType(defaultSelector.parameterTypeMap, returnType, annotationClass, true); + } + return cast(out); + } + + private static ParameterType selectByReturnType(Map> map, Class returnType, + Class annotationClass, boolean allowSubclass) { + ParameterType out = null; + if (allowSubclass) { + for (ParameterType type : map.values()) { + if (returnType.isAssignableFrom(type.getReturnType())) { + if (annotationClass == type.getAnnotationClass()) { + out = type; + break; + } + if (out == null) { + out = type; + } + } + } + } else { + for (ParameterType type : map.values()) { + if (returnType == type.getReturnType()) { + if (annotationClass == type.getAnnotationClass()) { + out = type; + break; + } + if (out == null) { + out = type; + } + } + } + } + return out; + } + + private static T cast(Object o) { + //noinspection unchecked + return (T) o; + } + + @Override + public void addType(boolean infolessAlias, ParameterType type) { + parameterTypeMap.put(type.getTypeKey(), type); + + if (infolessAlias) { + parameterTypeMap.putIfAbsent(type.getInfolessTypeKey(), type); + } + } + + static { + // registers default parameter types + ParameterTypes.clinit(); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java index dbd7590..d33932b 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterConfig.java @@ -1,80 +1,80 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.Reflection; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; - -/** - * 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 - * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information. - * - * @param the annotation type for parameters - * @param the object type that holds the information required in memory - */ -public abstract class ParameterConfig implements Comparable> { - private final Class annotationClass; - // protected final TParamInfo defaultValue; - - public ParameterConfig(Class annotationClass/*, TParamInfo defaultValue*/) { - this.annotationClass = annotationClass; - //this.defaultValue = defaultValue; - } - - public final Class getAnnotationClass() { - return annotationClass; - } - /* - public TParamInfo getDefaultValue() { - return defaultValue; - }*/ - - protected abstract TParamInfo toParameterInfo(TAnnotation annotation); - - public TParamInfo getParameterInfo(Annotation annotation) { - //noinspection unchecked - return toParameterInfo((TAnnotation) annotation); - } - - public static ParameterConfig - includeMemoryClass(Class annotationClass, Class memoryClass) { - Constructor constructor; - //TParamInfo defaultValue; - try { - constructor = memoryClass.getConstructor(annotationClass); - //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT"); - } catch (NoSuchMethodException | IllegalArgumentException ex) { - throw new IllegalArgumentException(ex); - } - /* - if (defaultValue == null) try { - defaultValue = memoryClass.newInstance(); - } catch (IllegalAccessException | InstantiationException ex) { - throw new IllegalArgumentException("Failed to get a default value for the param info", ex); - }*/ - - return new ParameterConfig(annotationClass/*, defaultValue*/) { - - @Override - public TParamInfo toParameterInfo(TAnnotation annotation) { - try { - return constructor.newInstance(annotation); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - }; - } - - public static ParameterConfig getMemoryClassFromField(Class annotationClass) { - return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS")); - } - - @Override - public int compareTo(ParameterConfig o) { - return 0; - } - -} - +package io.dico.dicore.command.parameter.type; + +import io.dico.dicore.Reflection; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; + +/** + * 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 + * that is separate from the annotation itself. This class acts as a bridge from the annotation to said class conveying the information. + * + * @param the annotation type for parameters + * @param the object type that holds the information required in memory + */ +public abstract class ParameterConfig implements Comparable> { + private final Class annotationClass; + // protected final TParamInfo defaultValue; + + public ParameterConfig(Class annotationClass/*, TParamInfo defaultValue*/) { + this.annotationClass = annotationClass; + //this.defaultValue = defaultValue; + } + + public final Class getAnnotationClass() { + return annotationClass; + } + /* + public TParamInfo getDefaultValue() { + return defaultValue; + }*/ + + protected abstract TParamInfo toParameterInfo(TAnnotation annotation); + + public TParamInfo getParameterInfo(Annotation annotation) { + //noinspection unchecked + return toParameterInfo((TAnnotation) annotation); + } + + public static ParameterConfig + includeMemoryClass(Class annotationClass, Class memoryClass) { + Constructor constructor; + //TParamInfo defaultValue; + try { + constructor = memoryClass.getConstructor(annotationClass); + //defaultValue = Reflection.getStaticFieldValue(annotationClass, "DEFAULT"); + } catch (NoSuchMethodException | IllegalArgumentException ex) { + throw new IllegalArgumentException(ex); + } + /* + if (defaultValue == null) try { + defaultValue = memoryClass.newInstance(); + } catch (IllegalAccessException | InstantiationException ex) { + throw new IllegalArgumentException("Failed to get a default value for the param info", ex); + }*/ + + return new ParameterConfig(annotationClass/*, defaultValue*/) { + + @Override + public TParamInfo toParameterInfo(TAnnotation annotation) { + try { + return constructor.newInstance(annotation); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }; + } + + public static ParameterConfig getMemoryClassFromField(Class annotationClass) { + return ParameterConfig.includeMemoryClass(annotationClass, Reflection.getStaticFieldValue(annotationClass, "MEMORY_CLASS")); + } + + @Override + public int compareTo(ParameterConfig o) { + return 0; + } + +} + diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java index e1a62fa..e9cca7f 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/ParameterType.java @@ -1,149 +1,149 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.Reflection; -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.annotation.Range; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * A parameter type. - * Takes care of parsing, default values as well as completions. - * - * @param type of the parameter - * @param the info object type for the parameter (Example: {@link Range.Memory} - */ -public abstract class ParameterType { - private final Class returnType; - private final ParameterConfig parameterConfig; - protected final ParameterType otherType; // flag or non-flag, depending on current - - public ParameterType(Class returnType) { - this(returnType, null); - } - - public ParameterType(Class returnType, ParameterConfig paramConfig) { - this.returnType = Objects.requireNonNull(returnType); - this.parameterConfig = paramConfig; - - ParameterType otherType = flagTypeParameter(); - this.otherType = otherType == null ? this : otherType; - } - - protected ParameterType(Class returnType, ParameterConfig parameterConfig, ParameterType otherType) { - this.returnType = returnType; - this.parameterConfig = parameterConfig; - this.otherType = otherType; - } - - public int getExpectedAmountOfConsumedArguments() { - return 1; - } - - public boolean canBeFlag() { - return this == otherType; - } - - public boolean isFlagExplicitly() { - return this instanceof FlagParameterType; - } - - /** - * @return The return type - */ - public final Class getReturnType() { - return returnType; - } - - public final Class getAnnotationClass() { - return parameterConfig == null ? null : parameterConfig.getAnnotationClass(); - } - - public final ParameterConfig getParameterConfig() { - return parameterConfig; - } - - public ParameterKey getTypeKey() { - return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null); - } - - public ParameterKey getInfolessTypeKey() { - return new ParameterKey(returnType, null); - } - - protected FlagParameterType flagTypeParameter() { - return null; - } - - public ParameterType asFlagParameter() { - return canBeFlag() ? this : otherType; - } - - public ParameterType asNormalParameter() { - return isFlagExplicitly() ? otherType : this; - } - - public abstract TReturn parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException; - - public TReturn parseForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return parse(parameter, context.getSender(), buffer); - } - - public TReturn getDefaultValue(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { - return null; - } - - public TReturn getDefaultValueForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - return getDefaultValue(parameter, context.getSender(), buffer); - } - - public List complete(Parameter parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { - return Collections.emptyList(); - } - - public List completeForContext(Parameter parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) { - return complete(parameter, context.getSender(), location, buffer); - } - - protected static abstract class FlagParameterType extends ParameterType { - - protected FlagParameterType(ParameterType otherType) { - super(otherType.returnType, otherType.parameterConfig, otherType); - } - - @Override - public int getExpectedAmountOfConsumedArguments() { - return otherType.getExpectedAmountOfConsumedArguments(); - } - - @Override - public boolean canBeFlag() { - return true; - } - - @Override - protected final FlagParameterType flagTypeParameter() { - return this; - } - - @Override - public ParameterType asFlagParameter() { - return this; - } - - @Override - public ParameterType asNormalParameter() { - return otherType; - } - - } - -} +package io.dico.dicore.command.parameter.type; + +import io.dico.dicore.Reflection; +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.annotation.Range; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.Parameter; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A parameter type. + * Takes care of parsing, default values as well as completions. + * + * @param type of the parameter + * @param the info object type for the parameter (Example: {@link Range.Memory} + */ +public abstract class ParameterType { + private final Class returnType; + private final ParameterConfig parameterConfig; + protected final ParameterType otherType; // flag or non-flag, depending on current + + public ParameterType(Class returnType) { + this(returnType, null); + } + + public ParameterType(Class returnType, ParameterConfig paramConfig) { + this.returnType = Objects.requireNonNull(returnType); + this.parameterConfig = paramConfig; + + ParameterType otherType = flagTypeParameter(); + this.otherType = otherType == null ? this : otherType; + } + + protected ParameterType(Class returnType, ParameterConfig parameterConfig, ParameterType otherType) { + this.returnType = returnType; + this.parameterConfig = parameterConfig; + this.otherType = otherType; + } + + public int getExpectedAmountOfConsumedArguments() { + return 1; + } + + public boolean canBeFlag() { + return this == otherType; + } + + public boolean isFlagExplicitly() { + return this instanceof FlagParameterType; + } + + /** + * @return The return type + */ + public final Class getReturnType() { + return returnType; + } + + public final Class getAnnotationClass() { + return parameterConfig == null ? null : parameterConfig.getAnnotationClass(); + } + + public final ParameterConfig getParameterConfig() { + return parameterConfig; + } + + public ParameterKey getTypeKey() { + return new ParameterKey(returnType, parameterConfig != null ? parameterConfig.getAnnotationClass() : null); + } + + public ParameterKey getInfolessTypeKey() { + return new ParameterKey(returnType, null); + } + + protected FlagParameterType flagTypeParameter() { + return null; + } + + public ParameterType asFlagParameter() { + return canBeFlag() ? this : otherType; + } + + public ParameterType asNormalParameter() { + return isFlagExplicitly() ? otherType : this; + } + + public abstract TReturn parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException; + + public TReturn parseForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + return parse(parameter, context.getSender(), buffer); + } + + public TReturn getDefaultValue(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { + return null; + } + + public TReturn getDefaultValueForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + return getDefaultValue(parameter, context.getSender(), buffer); + } + + public List complete(Parameter parameter, CommandSender sender, Location location, ArgumentBuffer buffer) { + return Collections.emptyList(); + } + + public List completeForContext(Parameter parameter, ExecutionContext context, Location location, ArgumentBuffer buffer) { + return complete(parameter, context.getSender(), location, buffer); + } + + protected static abstract class FlagParameterType extends ParameterType { + + protected FlagParameterType(ParameterType otherType) { + super(otherType.returnType, otherType.parameterConfig, otherType); + } + + @Override + public int getExpectedAmountOfConsumedArguments() { + return otherType.getExpectedAmountOfConsumedArguments(); + } + + @Override + public boolean canBeFlag() { + return true; + } + + @Override + protected final FlagParameterType flagTypeParameter() { + return this; + } + + @Override + public ParameterType asFlagParameter() { + return this; + } + + @Override + public ParameterType asNormalParameter() { + return otherType; + } + + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java index ecf19ce..667b2c7 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/parameter/type/SimpleParameterType.java @@ -1,31 +1,31 @@ -package io.dico.dicore.command.parameter.type; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import org.bukkit.command.CommandSender; - -/** - * An abstraction for parameter types that only parse a single argument - * - * @param the parameter type - * @param parameter info object type - */ -public abstract class SimpleParameterType extends ParameterType { - - public SimpleParameterType(Class returnType) { - super(returnType); - } - - public SimpleParameterType(Class returnType, ParameterConfig paramConfig) { - super(returnType, paramConfig); - } - - protected abstract TReturn parse(Parameter parameter, CommandSender sender, String input) throws CommandException; - - @Override - public TReturn parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { - return parse(parameter, sender, buffer.requireNext(parameter.getName())); - } - -} +package io.dico.dicore.command.parameter.type; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.Parameter; +import org.bukkit.command.CommandSender; + +/** + * An abstraction for parameter types that only parse a single argument + * + * @param the parameter type + * @param parameter info object type + */ +public abstract class SimpleParameterType extends ParameterType { + + public SimpleParameterType(Class returnType) { + super(returnType); + } + + public SimpleParameterType(Class returnType, ParameterConfig paramConfig) { + super(returnType, paramConfig); + } + + protected abstract TReturn parse(Parameter parameter, CommandSender sender, String input) throws CommandException; + + @Override + public TReturn parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { + return parse(parameter, sender, buffer.requireNext(parameter.getName())); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java index e664cef..6828fcc 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/DefaultGroupCommand.java @@ -1,56 +1,56 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.ExecutionContext; -import io.dico.dicore.command.IContextFilter; -import org.bukkit.command.CommandSender; - -public class DefaultGroupCommand extends PredefinedCommand { - private static final DefaultGroupCommand instance; - private static final IContextFilter noArgumentFilter; - - public static DefaultGroupCommand getInstance() { - return instance; - } - - private DefaultGroupCommand(boolean modifiable) { - addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - addContextFilter(noArgumentFilter); - this.modifiable = modifiable; - } - - public DefaultGroupCommand() { - this(true); - } - - @Override - protected DefaultGroupCommand newModifiableInstance() { - return new DefaultGroupCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1); - return null; - } - - static { - noArgumentFilter = new IContextFilter() { - @Override - public void filterContext(ExecutionContext context) throws CommandException { - if (context.getBuffer().hasNext()) { - throw new CommandException("No such command: /" + context.getAddress().getAddress() - + " " + context.getBuffer().next()); - } - } - - @Override - public Priority getPriority() { - return Priority.EARLY; - } - }; - - instance = new DefaultGroupCommand(false); - } - -} +package io.dico.dicore.command.predef; + +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.ExecutionContext; +import io.dico.dicore.command.IContextFilter; +import org.bukkit.command.CommandSender; + +public class DefaultGroupCommand extends PredefinedCommand { + private static final DefaultGroupCommand instance; + private static final IContextFilter noArgumentFilter; + + public static DefaultGroupCommand getInstance() { + return instance; + } + + private DefaultGroupCommand(boolean modifiable) { + addContextFilter(IContextFilter.INHERIT_PERMISSIONS); + addContextFilter(noArgumentFilter); + this.modifiable = modifiable; + } + + public DefaultGroupCommand() { + this(true); + } + + @Override + protected DefaultGroupCommand newModifiableInstance() { + return new DefaultGroupCommand(true); + } + + @Override + public String execute(CommandSender sender, ExecutionContext context) throws CommandException { + context.getAddress().getChatHandler().sendHelpMessage(sender, context, context.getAddress(), 1); + return null; + } + + static { + noArgumentFilter = new IContextFilter() { + @Override + public void filterContext(ExecutionContext context) throws CommandException { + if (context.getBuffer().hasNext()) { + throw new CommandException("No such command: /" + context.getAddress().getAddress() + + " " + context.getBuffer().next()); + } + } + + @Override + public Priority getPriority() { + return Priority.EARLY; + } + }; + + instance = new DefaultGroupCommand(false); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java index def0db1..985e055 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/HelpCommand.java @@ -1,76 +1,76 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.*; -import io.dico.dicore.command.annotation.Range; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.type.NumberParameterType; -import org.bukkit.command.CommandSender; - -/** - * The help command - */ -public class HelpCommand extends PredefinedCommand { - private static final Parameter pageParameter; - public static final HelpCommand INSTANCE; - - private HelpCommand(boolean modifiable) { - super(modifiable); - getParameterList().addParameter(pageParameter); - getParameterList().setRequiredCount(0); - setDescription("Shows this help page"); - } - - @Override - protected HelpCommand newModifiableInstance() { - return new HelpCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - ICommandAddress target = context.getAddress(); - if (context.getAddress().getCommand() == this) { - target = target.getParent(); - } - - context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.get("page") - 1); - return null; - } - - public static void registerAsChild(ICommandAddress address) { - registerAsChild(address, "help"); - } - - public static void registerAsChild(ICommandAddress address, String main, String... aliases) { - ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); - } - - static { - pageParameter = new Parameter<>("page", "the page number", - new NumberParameterType(Integer.TYPE) { - @Override - protected Integer parse(String input) throws NumberFormatException { - return Integer.parseInt(input); - } - - @Override - protected Integer select(Number number) { - return number.intValue(); - } - - @Override - public Integer parseForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { - if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) { - // An address was executed with its help command as target - buffer.next(); - return 1; - } - return parse(parameter, context.getSender(), buffer); - } - }, - new Range.Memory(1, Integer.MAX_VALUE, 1)); - - INSTANCE = new HelpCommand(false); - } - -} +package io.dico.dicore.command.predef; + +import io.dico.dicore.command.*; +import io.dico.dicore.command.annotation.Range; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.type.NumberParameterType; +import org.bukkit.command.CommandSender; + +/** + * The help command + */ +public class HelpCommand extends PredefinedCommand { + private static final Parameter pageParameter; + public static final HelpCommand INSTANCE; + + private HelpCommand(boolean modifiable) { + super(modifiable); + getParameterList().addParameter(pageParameter); + getParameterList().setRequiredCount(0); + setDescription("Shows this help page"); + } + + @Override + protected HelpCommand newModifiableInstance() { + return new HelpCommand(true); + } + + @Override + public String execute(CommandSender sender, ExecutionContext context) throws CommandException { + ICommandAddress target = context.getAddress(); + if (context.getAddress().getCommand() == this) { + target = target.getParent(); + } + + context.getAddress().getChatHandler().sendHelpMessage(sender, context, target, context.get("page") - 1); + return null; + } + + public static void registerAsChild(ICommandAddress address) { + registerAsChild(address, "help"); + } + + public static void registerAsChild(ICommandAddress address, String main, String... aliases) { + ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); + } + + static { + pageParameter = new Parameter<>("page", "the page number", + new NumberParameterType(Integer.TYPE) { + @Override + protected Integer parse(String input) throws NumberFormatException { + return Integer.parseInt(input); + } + + @Override + protected Integer select(Number number) { + return number.intValue(); + } + + @Override + public Integer parseForContext(Parameter parameter, ExecutionContext context, ArgumentBuffer buffer) throws CommandException { + if (context.getAddress().getCommand() == null || context.getAddress().getCommand().getClass() != HelpCommand.class) { + // An address was executed with its help command as target + buffer.next(); + return 1; + } + return parse(parameter, context.getSender(), buffer); + } + }, + new Range.Memory(1, Integer.MAX_VALUE, 1)); + + INSTANCE = new HelpCommand(false); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java index 4340356..b7c5587 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/PredefinedCommand.java @@ -1,50 +1,50 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.CommandBuilder; -import io.dico.dicore.command.ExtendedCommand; -import io.dico.dicore.command.ICommandAddress; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; - -/** - * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder} - */ -public abstract class PredefinedCommand> extends ExtendedCommand { - static final Map> predefinedCommandGenerators = new HashMap<>(); - - /** - * Get a predefined command - * - * @param name the name - * @return the subscriber - */ - public static Consumer getPredefinedCommandGenerator(String name) { - return predefinedCommandGenerators.get(name); - } - - /** - * Register a predefined command - * - * @param name the name - * @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) - */ - public static boolean registerPredefinedCommandGenerator(String name, Consumer consumer) { - return predefinedCommandGenerators.putIfAbsent(name, consumer) == null; - } - - static { - registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild); - //noinspection StaticInitializerReferencesSubClass - registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild); - } - - public PredefinedCommand() { - } - - public PredefinedCommand(boolean modifiable) { - super(modifiable); - } -} +package io.dico.dicore.command.predef; + +import io.dico.dicore.command.CommandBuilder; +import io.dico.dicore.command.ExtendedCommand; +import io.dico.dicore.command.ICommandAddress; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Marker class for commands that are generated. These commands can be replaced using methods in {@link CommandBuilder} + */ +public abstract class PredefinedCommand> extends ExtendedCommand { + static final Map> predefinedCommandGenerators = new HashMap<>(); + + /** + * Get a predefined command + * + * @param name the name + * @return the subscriber + */ + public static Consumer getPredefinedCommandGenerator(String name) { + return predefinedCommandGenerators.get(name); + } + + /** + * Register a predefined command + * + * @param name the name + * @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) + */ + public static boolean registerPredefinedCommandGenerator(String name, Consumer consumer) { + return predefinedCommandGenerators.putIfAbsent(name, consumer) == null; + } + + static { + registerPredefinedCommandGenerator("help", HelpCommand::registerAsChild); + //noinspection StaticInitializerReferencesSubClass + registerPredefinedCommandGenerator("syntax", SyntaxCommand::registerAsChild); + } + + public PredefinedCommand() { + } + + public PredefinedCommand(boolean modifiable) { + super(modifiable); + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java index 7ae8638..8532584 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/predef/SyntaxCommand.java @@ -1,36 +1,36 @@ -package io.dico.dicore.command.predef; - -import io.dico.dicore.command.*; -import org.bukkit.command.CommandSender; - -/** - * The syntax command - */ -public class SyntaxCommand extends PredefinedCommand { - public static final SyntaxCommand INSTANCE = new SyntaxCommand(false); - - private SyntaxCommand(boolean modifiable) { - super(modifiable); - setDescription("Describes how to use the command"); - } - - @Override - protected SyntaxCommand newModifiableInstance() { - return new SyntaxCommand(true); - } - - @Override - public String execute(CommandSender sender, ExecutionContext context) throws CommandException { - context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent()); - return null; - } - - public static void registerAsChild(ICommandAddress address) { - registerAsChild(address, "syntax"); - } - - public static void registerAsChild(ICommandAddress address, String main, String... aliases) { - ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); - } - -} +package io.dico.dicore.command.predef; + +import io.dico.dicore.command.*; +import org.bukkit.command.CommandSender; + +/** + * The syntax command + */ +public class SyntaxCommand extends PredefinedCommand { + public static final SyntaxCommand INSTANCE = new SyntaxCommand(false); + + private SyntaxCommand(boolean modifiable) { + super(modifiable); + setDescription("Describes how to use the command"); + } + + @Override + protected SyntaxCommand newModifiableInstance() { + return new SyntaxCommand(true); + } + + @Override + public String execute(CommandSender sender, ExecutionContext context) throws CommandException { + context.getAddress().getChatHandler().sendSyntaxMessage(sender, context, context.getAddress().getParent()); + return null; + } + + public static void registerAsChild(ICommandAddress address) { + registerAsChild(address, "syntax"); + } + + public static void registerAsChild(ICommandAddress address, String main, String... aliases) { + ((ModifiableCommandAddress) address).addChild(new ChildCommandAddress(INSTANCE, main, aliases)); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java index b5346d0..40de6ae 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/BukkitCommand.java @@ -1,122 +1,122 @@ -package io.dico.dicore.command.registration; - -import io.dico.dicore.command.ICommandAddress; -import io.dico.dicore.command.ICommandDispatcher; -import org.bukkit.Location; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.List; - -/** - * This class extends the bukkit's command class. - * Instances are injected into the command map. - */ -public class BukkitCommand extends Command { - private ICommandDispatcher dispatcher; - private ICommandAddress origin; - - public BukkitCommand(ICommandAddress address) { - super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size())); - this.dispatcher = address.getDispatcherForTree(); - this.origin = address; - - setTimingsIfNecessary(this); - } - - private static ICommandAddress validateTree(ICommandAddress tree) { - if (!tree.hasParent()) { - throw new IllegalArgumentException(); - } - if (tree.getNames().isEmpty()) { - throw new IllegalArgumentException(); - } - return tree; - } - - public ICommandAddress getOrigin() { - return origin; - } - - @Override - public boolean execute(CommandSender sender, String label, String[] args) { - if (!dispatcher.dispatchCommand(sender, label, args)) { - //System.out.println("failed to dispatch command"); - // target command not found, send a message in the future TODO - } - return true; - } - - @Override - public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - return this.tabComplete(sender, alias, args, null); - } - - //@Override - public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { - return dispatcher.getTabCompletions(sender, alias, location, args); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - BukkitCommand that = (BukkitCommand) o; - - return getName().equals(that.getName()) && dispatcher == that.dispatcher; - } - - @Override - public int hashCode() { - return dispatcher.hashCode() | getName().hashCode(); - } - - 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 - // I use reflection so that the project does not require paper spigot to build - try { - // public field - Field field = Command.class.getDeclaredField("timings"); - if (field.get(object) != null) return; - Class clazz = Class.forName("co.aikar.timings.TimingsManager"); - // public method - Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class); - Object timings = method.invoke(null, "", object); - field.set(object, timings); - } catch (Throwable ignored) { - } - } - - /* - public static void registerToMap(ICommandAddress tree, Map map) { - BukkitCommand command = new BukkitCommand(tree); - Iterator iterator = tree.getNames().iterator(); - map.put(iterator.next(), command); - while (iterator.hasNext()) { - map.putIfAbsent(iterator.next(), command); - } - } - - public static void unregisterFromMap(ICommandAddress tree, Map map) { - map.values().remove(new BukkitCommand(tree)); - } - - public static void registerChildrenToMap(ICommandAddress tree, Map map) { - for (Map.Entry entry : tree.getChildren().entrySet()) { - ICommandAddress child = entry.getValue(); - registerToMap(child, map); - } - } - - public static void unregisterChildenFromMap(ICommandAddress tree, Map map) { - for (Map.Entry entry : tree.getChildren().entrySet()) { - ICommandAddress child = entry.getValue(); - unregisterFromMap(child, map); - } - } - */ - -} +package io.dico.dicore.command.registration; + +import io.dico.dicore.command.ICommandAddress; +import io.dico.dicore.command.ICommandDispatcher; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +/** + * This class extends the bukkit's command class. + * Instances are injected into the command map. + */ +public class BukkitCommand extends Command { + private ICommandDispatcher dispatcher; + private ICommandAddress origin; + + public BukkitCommand(ICommandAddress address) { + super(validateTree(address).getNames().get(0), "", "", address.getNames().subList(1, address.getNames().size())); + this.dispatcher = address.getDispatcherForTree(); + this.origin = address; + + setTimingsIfNecessary(this); + } + + private static ICommandAddress validateTree(ICommandAddress tree) { + if (!tree.hasParent()) { + throw new IllegalArgumentException(); + } + if (tree.getNames().isEmpty()) { + throw new IllegalArgumentException(); + } + return tree; + } + + public ICommandAddress getOrigin() { + return origin; + } + + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + if (!dispatcher.dispatchCommand(sender, label, args)) { + //System.out.println("failed to dispatch command"); + // target command not found, send a message in the future TODO + } + return true; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + return this.tabComplete(sender, alias, args, null); + } + + //@Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + return dispatcher.getTabCompletions(sender, alias, location, args); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BukkitCommand that = (BukkitCommand) o; + + return getName().equals(that.getName()) && dispatcher == that.dispatcher; + } + + @Override + public int hashCode() { + return dispatcher.hashCode() | getName().hashCode(); + } + + 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 + // I use reflection so that the project does not require paper spigot to build + try { + // public field + Field field = Command.class.getDeclaredField("timings"); + if (field.get(object) != null) return; + Class clazz = Class.forName("co.aikar.timings.TimingsManager"); + // public method + Method method = clazz.getDeclaredMethod("getCommandTiming", String.class, Command.class); + Object timings = method.invoke(null, "", object); + field.set(object, timings); + } catch (Throwable ignored) { + } + } + + /* + public static void registerToMap(ICommandAddress tree, Map map) { + BukkitCommand command = new BukkitCommand(tree); + Iterator iterator = tree.getNames().iterator(); + map.put(iterator.next(), command); + while (iterator.hasNext()) { + map.putIfAbsent(iterator.next(), command); + } + } + + public static void unregisterFromMap(ICommandAddress tree, Map map) { + map.values().remove(new BukkitCommand(tree)); + } + + public static void registerChildrenToMap(ICommandAddress tree, Map map) { + for (Map.Entry entry : tree.getChildren().entrySet()) { + ICommandAddress child = entry.getValue(); + registerToMap(child, map); + } + } + + public static void unregisterChildenFromMap(ICommandAddress tree, Map map) { + for (Map.Entry entry : tree.getChildren().entrySet()) { + ICommandAddress child = entry.getValue(); + unregisterFromMap(child, map); + } + } + */ + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java index 780ec66..2008b29 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/CommandMap.java @@ -1,59 +1,59 @@ -package io.dico.dicore.command.registration; - -import io.dico.dicore.Reflection; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.SimpleCommandMap; -import org.bukkit.plugin.SimplePluginManager; - -import java.util.*; - -/** - * Provides access to bukkit's {@code Map} command map. - */ -@SuppressWarnings("ConstantConditions") -public class CommandMap { - private static final Map commandMap = findCommandMap(); - - private CommandMap() { - - } - - public static Map getCommandMap() { - return Objects.requireNonNull(commandMap); - } - - public static boolean isAvailable() { - return commandMap != null; - } - - public static Command get(String key) { - return commandMap.get(key); - } - - public static void put(String key, Command command) { - commandMap.put(key, command); - } - - public static Collection replace(Command command, Command replacement) { - List result = new ArrayList<>(); - for (Map.Entry entry : commandMap.entrySet()) { - if (entry.getValue() == command) { - entry.setValue(replacement); - result.add(entry.getKey()); - } - } - return result; - } - - private static Map findCommandMap() { - try { - return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands", - Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager())); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - -} +package io.dico.dicore.command.registration; + +import io.dico.dicore.Reflection; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.plugin.SimplePluginManager; + +import java.util.*; + +/** + * Provides access to bukkit's {@code Map} command map. + */ +@SuppressWarnings("ConstantConditions") +public class CommandMap { + private static final Map commandMap = findCommandMap(); + + private CommandMap() { + + } + + public static Map getCommandMap() { + return Objects.requireNonNull(commandMap); + } + + public static boolean isAvailable() { + return commandMap != null; + } + + public static Command get(String key) { + return commandMap.get(key); + } + + public static void put(String key, Command command) { + commandMap.put(key, command); + } + + public static Collection replace(Command command, Command replacement) { + List result = new ArrayList<>(); + for (Map.Entry entry : commandMap.entrySet()) { + if (entry.getValue() == command) { + entry.setValue(replacement); + result.add(entry.getKey()); + } + } + return result; + } + + private static Map findCommandMap() { + try { + return Reflection.getFieldValue(SimpleCommandMap.class, "knownCommands", + Reflection.getFieldValue(SimplePluginManager.class, "commandMap", Bukkit.getPluginManager())); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java index 478c70c..0b7ce3c 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/CommandParseException.java @@ -1,27 +1,27 @@ -package io.dico.dicore.command.registration.reflect; - -/** - * 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. - */ -public class CommandParseException extends Exception { - - public CommandParseException() { - } - - public CommandParseException(String message) { - super(message); - } - - public CommandParseException(String message, Throwable cause) { - super(message, cause); - } - - public CommandParseException(Throwable cause) { - super(cause); - } - - public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} +package io.dico.dicore.command.registration.reflect; + +/** + * 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. + */ +public class CommandParseException extends Exception { + + public CommandParseException() { + } + + public CommandParseException(String message) { + super(message); + } + + public CommandParseException(String message, Throwable cause) { + super(message, cause); + } + + public CommandParseException(Throwable cause) { + super(cause); + } + + public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java index 67b65e4..1e56b8a 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandInterceptor.java @@ -1,36 +1,36 @@ -package io.dico.dicore.command.registration.reflect; - -import io.dico.dicore.command.ExecutionContext; - -import java.lang.reflect.Method; - -public interface ICommandInterceptor { - - /** - * Get the receiver of the command, if applicable. - * A command has a receiver if its first parameter implements {@link ICommandReceiver} - * and its instance object implements this interface. - * - * @param context the context of execution - * @param target the method of the command - * @param cmdName the name of the command - * @return the receiver - */ - default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) { - return null; - } - - /** - * 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. - * - * @param context the context of execution - * @param target the method of the command - * @param cmdName the name of the command - * @return the coroutine context - */ - default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) { - return null; - } - -} +package io.dico.dicore.command.registration.reflect; + +import io.dico.dicore.command.ExecutionContext; + +import java.lang.reflect.Method; + +public interface ICommandInterceptor { + + /** + * Get the receiver of the command, if applicable. + * A command has a receiver if its first parameter implements {@link ICommandReceiver} + * and its instance object implements this interface. + * + * @param context the context of execution + * @param target the method of the command + * @param cmdName the name of the command + * @return the receiver + */ + default ICommandReceiver getReceiver(ExecutionContext context, Method target, String cmdName) { + return null; + } + + /** + * 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. + * + * @param context the context of execution + * @param target the method of the command + * @param cmdName the name of the command + * @return the coroutine context + */ + default Object getCoroutineContext(ExecutionContext context, Method target, String cmdName) { + return null; + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java index bdcb568..d0681b1 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ICommandReceiver.java @@ -1,5 +1,5 @@ -package io.dico.dicore.command.registration.reflect; - -public interface ICommandReceiver { - -} +package io.dico.dicore.command.registration.reflect; + +public interface ICommandReceiver { + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java index 2d7c333..34ea8de 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveCommand.java @@ -1,187 +1,187 @@ -package io.dico.dicore.command.registration.reflect; - -import io.dico.dicore.command.*; -import io.dico.dicore.command.annotation.Cmd; -import io.dico.dicore.command.annotation.GenerateCommands; -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; -import org.bukkit.command.CommandSender; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - -public final class ReflectiveCommand extends Command { - private static final int continuationMask = 1 << 3; - private final Cmd cmdAnnotation; - private final Method method; - private final Object instance; - private String[] parameterOrder; - - // hasContinuation | hasContext | hasSender | hasReceiver - private final int flags; - - ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { - if (!method.isAnnotationPresent(Cmd.class)) { - throw new CommandParseException("No @Cmd present for the method " + method.toGenericString()); - } - cmdAnnotation = method.getAnnotation(Cmd.class); - - java.lang.reflect.Parameter[] parameters = method.getParameters(); - - if (!method.isAccessible()) try { - method.setAccessible(true); - } catch (Exception ex) { - throw new CommandParseException("Failed to make method accessible"); - } - - if (!Modifier.isStatic(method.getModifiers())) { - if (instance == null) { - try { - instance = method.getDeclaringClass().newInstance(); - } catch (Exception ex) { - throw new CommandParseException("No instance given for instance method, and failed to create new instance", ex); - } - } else if (!method.getDeclaringClass().isInstance(instance)) { - throw new CommandParseException("Given instance is not an instance of the method's declaring class"); - } - } - - this.method = method; - this.instance = instance; - this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters); - } - - public Method getMethod() { - return method; - } - - public Object getInstance() { - return instance; - } - - public String getCmdName() { return cmdAnnotation.value(); } - - void setParameterOrder(String[] parameterOrder) { - this.parameterOrder = parameterOrder; - } - - ICommandAddress getAddress() { - ChildCommandAddress result = new ChildCommandAddress(); - result.setCommand(this); - - Cmd cmd = cmdAnnotation; - result.getNames().add(cmd.value()); - for (String alias : cmd.aliases()) { - result.getNames().add(alias); - } - result.finalizeNames(); - - GenerateCommands generateCommands = method.getAnnotation(GenerateCommands.class); - if (generateCommands != null) { - ReflectiveRegistration.generateCommands(result, generateCommands.value()); - } - - return result; - } - - @Override - 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]; - - int i = 0; - - int mask = 1; - if ((flags & mask) != 0) { - // Has receiver - try { - args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName()); - } catch (Exception ex) { - handleException(ex); - return null; // unreachable - } - } - - mask <<= 1; - 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() { - try { - return KotlinReflectiveRegistrationKt.isSuspendFunction(method); - } catch (Throwable ex) { - return false; - } - } - - public String callSynchronously(Object[] args) throws CommandException { - try { - return getResult(method.invoke(instance, args), null); - } catch (Exception ex) { - return getResult(null, ex); - } - } - - public static String getResult(Object returned, Exception ex) throws CommandException { - if (ex != null) { - handleException(ex); - return null; // unreachable - } - - if (returned instanceof String) { - return (String) returned; - } - return null; - } - - public static void handleException(Exception ex) throws CommandException { - if (ex instanceof InvocationTargetException) { - if (ex.getCause() instanceof CommandException) { - throw (CommandException) ex.getCause(); - } - - ex.printStackTrace(); - throw new CommandException("An internal error occurred while executing this command.", ex); - } - if (ex instanceof CommandException) { - throw (CommandException) ex; - } - ex.printStackTrace(); - throw new CommandException("An internal error occurred while executing this command.", ex); - } - - private String callAsCoroutine(ExecutionContext context, Object[] args) { - return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args); - } - -} +package io.dico.dicore.command.registration.reflect; + +import io.dico.dicore.command.*; +import io.dico.dicore.command.annotation.Cmd; +import io.dico.dicore.command.annotation.GenerateCommands; +import io.dico.dicore.command.parameter.type.IParameterTypeSelector; +import org.bukkit.command.CommandSender; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public final class ReflectiveCommand extends Command { + private static final int continuationMask = 1 << 3; + private final Cmd cmdAnnotation; + private final Method method; + private final Object instance; + private String[] parameterOrder; + + // hasContinuation | hasContext | hasSender | hasReceiver + private final int flags; + + ReflectiveCommand(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { + if (!method.isAnnotationPresent(Cmd.class)) { + throw new CommandParseException("No @Cmd present for the method " + method.toGenericString()); + } + cmdAnnotation = method.getAnnotation(Cmd.class); + + java.lang.reflect.Parameter[] parameters = method.getParameters(); + + if (!method.isAccessible()) try { + method.setAccessible(true); + } catch (Exception ex) { + throw new CommandParseException("Failed to make method accessible"); + } + + if (!Modifier.isStatic(method.getModifiers())) { + if (instance == null) { + try { + instance = method.getDeclaringClass().newInstance(); + } catch (Exception ex) { + throw new CommandParseException("No instance given for instance method, and failed to create new instance", ex); + } + } else if (!method.getDeclaringClass().isInstance(instance)) { + throw new CommandParseException("Given instance is not an instance of the method's declaring class"); + } + } + + this.method = method; + this.instance = instance; + this.flags = ReflectiveRegistration.parseCommandAttributes(selector, method, this, parameters); + } + + public Method getMethod() { + return method; + } + + public Object getInstance() { + return instance; + } + + public String getCmdName() { return cmdAnnotation.value(); } + + void setParameterOrder(String[] parameterOrder) { + this.parameterOrder = parameterOrder; + } + + ICommandAddress getAddress() { + ChildCommandAddress result = new ChildCommandAddress(); + result.setCommand(this); + + Cmd cmd = cmdAnnotation; + result.getNames().add(cmd.value()); + for (String alias : cmd.aliases()) { + result.getNames().add(alias); + } + result.finalizeNames(); + + GenerateCommands generateCommands = method.getAnnotation(GenerateCommands.class); + if (generateCommands != null) { + ReflectiveRegistration.generateCommands(result, generateCommands.value()); + } + + return result; + } + + @Override + 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]; + + int i = 0; + + int mask = 1; + if ((flags & mask) != 0) { + // Has receiver + try { + args[i++] = ((ICommandInterceptor) instance).getReceiver(context, method, getCmdName()); + } catch (Exception ex) { + handleException(ex); + return null; // unreachable + } + } + + mask <<= 1; + 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() { + try { + return KotlinReflectiveRegistrationKt.isSuspendFunction(method); + } catch (Throwable ex) { + return false; + } + } + + public String callSynchronously(Object[] args) throws CommandException { + try { + return getResult(method.invoke(instance, args), null); + } catch (Exception ex) { + return getResult(null, ex); + } + } + + public static String getResult(Object returned, Exception ex) throws CommandException { + if (ex != null) { + handleException(ex); + return null; // unreachable + } + + if (returned instanceof String) { + return (String) returned; + } + return null; + } + + public static void handleException(Exception ex) throws CommandException { + if (ex instanceof InvocationTargetException) { + if (ex.getCause() instanceof CommandException) { + throw (CommandException) ex.getCause(); + } + + ex.printStackTrace(); + throw new CommandException("An internal error occurred while executing this command.", ex); + } + if (ex instanceof CommandException) { + throw (CommandException) ex; + } + ex.printStackTrace(); + throw new CommandException("An internal error occurred while executing this command.", ex); + } + + private String callAsCoroutine(ExecutionContext context, Object[] args) { + return KotlinReflectiveRegistrationKt.callAsCoroutine(this, (ICommandInterceptor) instance, context, args); + } + +} diff --git a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java index 6b1965d..93ac0ee 100644 --- a/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java +++ b/dicore3/command/src/main/java/io/dico/dicore/command/registration/reflect/ReflectiveRegistration.java @@ -1,415 +1,415 @@ -package io.dico.dicore.command.registration.reflect; - -import io.dico.dicore.command.*; -import io.dico.dicore.command.annotation.*; -import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.ParameterList; -import io.dico.dicore.command.parameter.type.IParameterTypeSelector; -import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; -import io.dico.dicore.command.parameter.type.ParameterType; -import io.dico.dicore.command.parameter.type.ParameterTypes; -import io.dico.dicore.command.predef.PredefinedCommand; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; -import java.util.function.Consumer; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** - * Takes care of turning a reflection {@link Method} into a command and more. - */ -public class ReflectiveRegistration { - /** - * This object provides names of the parameters. - * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without - * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to - * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does. - * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names - * or, finally, to get the Jvm-provided parameter names. - */ - //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer()); - @SuppressWarnings("StatementWithEmptyBody") - private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) { - int n = parameters.length; - String[] out = new String[n - start]; - - //String[] bytecode; - //try { - // bytecode = paranamer.lookupParameterNames(method, false); - //} catch (Exception ex) { - // bytecode = new String[0]; - // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode"); - // //ex.printStackTrace(); - //} - //int bn = bytecode.length; - - for (int i = start; i < n; i++) { - java.lang.reflect.Parameter parameter = parameters[i]; - Flag flag = parameter.getAnnotation(Flag.class); - NamedArg namedArg = parameter.getAnnotation(NamedArg.class); - - boolean isFlag = flag != null; - String name; - if (namedArg != null && !(name = namedArg.value()).isEmpty()) { - } else if (isFlag && !(name = flag.value()).isEmpty()) { - //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) { - } else { - name = parameter.getName(); - } - - if (isFlag) { - name = '-' + name; - } else { - int idx = 0; - while (name.startsWith("-", idx)) { - idx++; - } - name = name.substring(idx); - } - - out[i - start] = name; - } - - return out; - } - - public static void parseCommandGroup(ICommandAddress address, Class clazz, Object instance) throws CommandParseException { - parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance); - } - - public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class clazz, Object instance) throws CommandParseException { - boolean requireStatic = instance == null; - if (!requireStatic && !clazz.isInstance(instance)) { - throw new CommandParseException(); - } - - List methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); - - Iterator it = methods.iterator(); - for (Method method; it.hasNext(); ) { - method = it.next(); - - if (requireStatic && !Modifier.isStatic(method.getModifiers())) { - it.remove(); - continue; - } - - if (method.isAnnotationPresent(CmdParamType.class)) { - it.remove(); - - if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) { - throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments"); - } - - ParameterType type; - try { - Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance; - type = (ParameterType) method.invoke(inst); - Objects.requireNonNull(type, "ParameterType returned is null"); - } catch (Exception ex) { - throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex); - } - - if (selector == ParameterTypes.getSelector()) { - selector = new MapBasedParameterTypeSelector(true); - } - - selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type); - } - } - - GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address); - for (Method method : methods) { - if (method.isAnnotationPresent(Cmd.class)) { - ICommandAddress parsed = parseCommandMethod(selector, method, instance); - groupMatcherCache.getGroupFor(method).addChild(parsed); - } - } - - } - - private static final class GroupMatcherCache { - private ModifiableCommandAddress groupRootAddress; - private GroupEntry[] matchEntries; - private Pattern[] patterns; - private ModifiableCommandAddress[] addresses; - - GroupMatcherCache(Class clazz, ICommandAddress groupRootAddress) throws CommandParseException { - this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress; - - GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class); - GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value(); - - Pattern[] patterns = new Pattern[matchEntries.length]; - for (int i = 0; i < matchEntries.length; i++) { - GroupEntry matchEntry = matchEntries[i]; - if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) { - throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry"); - } - try { - patterns[i] = Pattern.compile(matchEntry.regex()); - } catch (PatternSyntaxException ex) { - throw new CommandParseException(ex); - } - } - - this.matchEntries = matchEntries; - this.patterns = patterns; - this.addresses = new ModifiableCommandAddress[this.matchEntries.length]; - } - - ModifiableCommandAddress getGroupFor(Method method) { - String name = method.getName(); - - GroupEntry[] matchEntries = this.matchEntries; - Pattern[] patterns = this.patterns; - ModifiableCommandAddress[] addresses = this.addresses; - - for (int i = 0; i < matchEntries.length; i++) { - GroupEntry matchEntry = matchEntries[i]; - if (patterns[i].matcher(name).matches()) { - if (addresses[i] == null) { - ChildCommandAddress placeholder = new ChildCommandAddress(); - placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases()); - addresses[i] = placeholder; - groupRootAddress.addChild(placeholder); - generateCommands(placeholder, matchEntry.generatedCommands()); - setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription()); - } - return addresses[i]; - } - } - - return groupRootAddress; - } - - } - - public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { - return new ReflectiveCommand(selector, method, instance).getAddress(); - } - - static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException { - 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; - - if (parameters.length > start - && command.getInstance() instanceof ICommandInterceptor - && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) { - hasReceiverParameter = true; - start++; - } - - if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) { - hasSenderParameter = true; - start++; - } - - if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) { - hasContextParameter = true; - start++; - } - - if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { - hasContinuationParameter = true; - end--; - } - - String[] parameterNames = lookupParameterNames(method, parameters, start); - for (int i = start, n = end; i < n; i++) { - Parameter parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]); - list.addParameter(parameter); - } - command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames); - - RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class); - if (cmdPermissions != null) { - for (String permission : cmdPermissions.value()) { - command.addContextFilter(IContextFilter.permission(permission)); - } - - if (cmdPermissions.inherit()) { - command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - } - } else { - command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); - } - - RequireParameters reqPar = method.getAnnotation(RequireParameters.class); - if (reqPar != null) { - list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value()); - } else { - list.setRequiredCount(list.getIndexedParameters().size()); - } - - /* - PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class); - if (preprocessArgs != null) { - IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar()); - list.setArgumentPreProcessor(preProcessor); - }*/ - - Desc desc = method.getAnnotation(Desc.class); - if (desc != null) { - String[] array = desc.value(); - if (array.length == 0) { - command.setDescription(desc.shortVersion()); - } else { - command.setDescription(array); - } - } else { - command.setDescription(); - } - - if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) { - command.addContextFilter(IContextFilter.PLAYER_ONLY); - } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) { - command.addContextFilter(IContextFilter.CONSOLE_ONLY); - } else if (method.isAnnotationPresent(RequirePlayer.class)) { - command.addContextFilter(IContextFilter.PLAYER_ONLY); - } else if (method.isAnnotationPresent(RequireConsole.class)) { - command.addContextFilter(IContextFilter.CONSOLE_ONLY); - } - - list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs()); - 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; - } - - public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException { - return parseCommandAttributes(selector, method, command, method.getParameters()); - } - - public static Parameter parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException { - Class type = parameter.getType(); - if (parameter.isVarArgs()) { - type = type.getComponentType(); - } - - Annotation[] annotations = parameter.getAnnotations(); - Flag flag = null; - Annotation typeAnnotation = null; - Desc desc = null; - - for (Annotation annotation : annotations) { - //noinspection StatementWithEmptyBody - if (annotation instanceof NamedArg) { - // do nothing - } else if (annotation instanceof Flag) { - if (flag != null) { - throw new CommandParseException("Multiple flags for the same parameter"); - } - flag = (Flag) annotation; - } else if (annotation instanceof Desc) { - if (desc != null) { - throw new CommandParseException("Multiple descriptions for the same parameter"); - } - desc = (Desc) annotation; - } else { - if (typeAnnotation != null) { - throw new CommandParseException("Multiple parameter type annotations for the same parameter"); - } - typeAnnotation = annotation; - } - } - - if (flag == null && name.startsWith("-")) { - throw new CommandParseException("Non-flag parameter's name starts with -"); - } else if (flag != null && !name.startsWith("-")) { - throw new CommandParseException("Flag parameter's name doesn't start with -"); - } - - ParameterType parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass()); - if (parameterType == null) { - throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString()); - } - - Object parameterInfo; - if (typeAnnotation == null) { - parameterInfo = null; - } else try { - parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation); - } catch (Exception ex) { - throw new CommandParseException("Invalid parameter config", ex); - } - - String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc); - - try { - //noinspection unchecked - String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission(); - return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission); - } catch (Exception ex) { - throw new CommandParseException("Invalid parameter", ex); - } - } - - public static void generateCommands(ICommandAddress address, String[] input) { - for (String value : input) { - Consumer consumer = PredefinedCommand.getPredefinedCommandGenerator(value); - if (consumer == null) { - System.out.println("[Command Warning] generated command '" + value + "' could not be found"); - } else { - consumer.accept(address); - } - } - } - - /* - Desired format - - @Cmd({"tp", "tpto"}) - @RequirePermissions("teleport.self") - public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) { - Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore"); - sender.teleport(target); - //return - } - - parser needs to: - - see the @Cmd and create a CommandTree for it - - see that it must be a Player executing the command - - add an indexed IParameter for a Player type - - add a flag parameter named force, that consumes no arguments. - - see that setting the force flag requires a permission - */ - - private static void setDescription(ICommandAddress address, String[] array, String shortVersion) { - if (!address.hasCommand()) { - return; - } - - if (array.length == 0) { - address.getCommand().setDescription(shortVersion); - } else { - address.getCommand().setDescription(array); - } - - } - -} +package io.dico.dicore.command.registration.reflect; + +import io.dico.dicore.command.*; +import io.dico.dicore.command.annotation.*; +import io.dico.dicore.command.annotation.GroupMatchedCommands.GroupEntry; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.ParameterList; +import io.dico.dicore.command.parameter.type.IParameterTypeSelector; +import io.dico.dicore.command.parameter.type.MapBasedParameterTypeSelector; +import io.dico.dicore.command.parameter.type.ParameterType; +import io.dico.dicore.command.parameter.type.ParameterTypes; +import io.dico.dicore.command.predef.PredefinedCommand; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.function.Consumer; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * Takes care of turning a reflection {@link Method} into a command and more. + */ +public class ReflectiveRegistration { + /** + * This object provides names of the parameters. + * Oddly, the AnnotationParanamer extensions require a 'fallback' paranamer to function properly without + * requiring ALL parameters to have that flag. This is weird because it should just use the AdaptiveParanamer on an upper level to + * determine the name of each individual flag. Oddly this isn't how it works, so the fallback works the same way as the AdaptiveParanamer does. + * It's just linked instead of using an array for that part. Then we can use an AdaptiveParanamer for the latest fallback, to get bytecode names + * or, finally, to get the Jvm-provided parameter names. + */ + //private static final Paranamer paranamer = new CachingParanamer(new BytecodeReadingParanamer()); + @SuppressWarnings("StatementWithEmptyBody") + private static String[] lookupParameterNames(Method method, java.lang.reflect.Parameter[] parameters, int start) { + int n = parameters.length; + String[] out = new String[n - start]; + + //String[] bytecode; + //try { + // bytecode = paranamer.lookupParameterNames(method, false); + //} catch (Exception ex) { + // bytecode = new String[0]; + // System.err.println("ReflectiveRegistration.lookupParameterNames failed to read bytecode"); + // //ex.printStackTrace(); + //} + //int bn = bytecode.length; + + for (int i = start; i < n; i++) { + java.lang.reflect.Parameter parameter = parameters[i]; + Flag flag = parameter.getAnnotation(Flag.class); + NamedArg namedArg = parameter.getAnnotation(NamedArg.class); + + boolean isFlag = flag != null; + String name; + if (namedArg != null && !(name = namedArg.value()).isEmpty()) { + } else if (isFlag && !(name = flag.value()).isEmpty()) { + //} else if (i < bn && (name = bytecode[i]) != null && !name.isEmpty()) { + } else { + name = parameter.getName(); + } + + if (isFlag) { + name = '-' + name; + } else { + int idx = 0; + while (name.startsWith("-", idx)) { + idx++; + } + name = name.substring(idx); + } + + out[i - start] = name; + } + + return out; + } + + public static void parseCommandGroup(ICommandAddress address, Class clazz, Object instance) throws CommandParseException { + parseCommandGroup(address, ParameterTypes.getSelector(), clazz, instance); + } + + public static void parseCommandGroup(ICommandAddress address, IParameterTypeSelector selector, Class clazz, Object instance) throws CommandParseException { + boolean requireStatic = instance == null; + if (!requireStatic && !clazz.isInstance(instance)) { + throw new CommandParseException(); + } + + List methods = new LinkedList<>(Arrays.asList(clazz.getDeclaredMethods())); + + Iterator it = methods.iterator(); + for (Method method; it.hasNext(); ) { + method = it.next(); + + if (requireStatic && !Modifier.isStatic(method.getModifiers())) { + it.remove(); + continue; + } + + if (method.isAnnotationPresent(CmdParamType.class)) { + it.remove(); + + if (method.getReturnType() != ParameterType.class || method.getParameterCount() != 0) { + throw new CommandParseException("Invalid CmdParamType method: must return ParameterType and take no arguments"); + } + + ParameterType type; + try { + Object inst = Modifier.isStatic(method.getModifiers()) ? null : instance; + type = (ParameterType) method.invoke(inst); + Objects.requireNonNull(type, "ParameterType returned is null"); + } catch (Exception ex) { + throw new CommandParseException("Error occurred whilst getting ParameterType from CmdParamType method '" + method.toGenericString() + "'", ex); + } + + if (selector == ParameterTypes.getSelector()) { + selector = new MapBasedParameterTypeSelector(true); + } + + selector.addType(method.getAnnotation(CmdParamType.class).infolessAlias(), type); + } + } + + GroupMatcherCache groupMatcherCache = new GroupMatcherCache(clazz, address); + for (Method method : methods) { + if (method.isAnnotationPresent(Cmd.class)) { + ICommandAddress parsed = parseCommandMethod(selector, method, instance); + groupMatcherCache.getGroupFor(method).addChild(parsed); + } + } + + } + + private static final class GroupMatcherCache { + private ModifiableCommandAddress groupRootAddress; + private GroupEntry[] matchEntries; + private Pattern[] patterns; + private ModifiableCommandAddress[] addresses; + + GroupMatcherCache(Class clazz, ICommandAddress groupRootAddress) throws CommandParseException { + this.groupRootAddress = (ModifiableCommandAddress) groupRootAddress; + + GroupMatchedCommands groupMatchedCommands = clazz.getAnnotation(GroupMatchedCommands.class); + GroupEntry[] matchEntries = groupMatchedCommands == null ? new GroupEntry[0] : groupMatchedCommands.value(); + + Pattern[] patterns = new Pattern[matchEntries.length]; + for (int i = 0; i < matchEntries.length; i++) { + GroupEntry matchEntry = matchEntries[i]; + if (matchEntry.group().isEmpty() || matchEntry.regex().isEmpty()) { + throw new CommandParseException("Empty group or regex in GroupMatchedCommands entry"); + } + try { + patterns[i] = Pattern.compile(matchEntry.regex()); + } catch (PatternSyntaxException ex) { + throw new CommandParseException(ex); + } + } + + this.matchEntries = matchEntries; + this.patterns = patterns; + this.addresses = new ModifiableCommandAddress[this.matchEntries.length]; + } + + ModifiableCommandAddress getGroupFor(Method method) { + String name = method.getName(); + + GroupEntry[] matchEntries = this.matchEntries; + Pattern[] patterns = this.patterns; + ModifiableCommandAddress[] addresses = this.addresses; + + for (int i = 0; i < matchEntries.length; i++) { + GroupEntry matchEntry = matchEntries[i]; + if (patterns[i].matcher(name).matches()) { + if (addresses[i] == null) { + ChildCommandAddress placeholder = new ChildCommandAddress(); + placeholder.setupAsPlaceholder(matchEntry.group(), matchEntry.groupAliases()); + addresses[i] = placeholder; + groupRootAddress.addChild(placeholder); + generateCommands(placeholder, matchEntry.generatedCommands()); + setDescription(placeholder, matchEntry.description(), matchEntry.shortDescription()); + } + return addresses[i]; + } + } + + return groupRootAddress; + } + + } + + public static ICommandAddress parseCommandMethod(IParameterTypeSelector selector, Method method, Object instance) throws CommandParseException { + return new ReflectiveCommand(selector, method, instance).getAddress(); + } + + static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command, java.lang.reflect.Parameter[] parameters) throws CommandParseException { + 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; + + if (parameters.length > start + && command.getInstance() instanceof ICommandInterceptor + && ICommandReceiver.class.isAssignableFrom(parameters[start].getType())) { + hasReceiverParameter = true; + start++; + } + + if (parameters.length > start && CommandSender.class.isAssignableFrom(senderParameterType = parameters[start].getType())) { + hasSenderParameter = true; + start++; + } + + if (parameters.length > start && parameters[start].getType() == ExecutionContext.class) { + hasContextParameter = true; + start++; + } + + if (parameters.length > start && parameters[end - 1].getType().getName().equals("kotlin.coroutines.Continuation")) { + hasContinuationParameter = true; + end--; + } + + String[] parameterNames = lookupParameterNames(method, parameters, start); + for (int i = start, n = end; i < n; i++) { + Parameter parameter = parseParameter(selector, method, parameters[i], parameterNames[i - start]); + list.addParameter(parameter); + } + command.setParameterOrder(hasContinuationParameter ? Arrays.copyOfRange(parameterNames, 0, parameterNames.length - 1) : parameterNames); + + RequirePermissions cmdPermissions = method.getAnnotation(RequirePermissions.class); + if (cmdPermissions != null) { + for (String permission : cmdPermissions.value()) { + command.addContextFilter(IContextFilter.permission(permission)); + } + + if (cmdPermissions.inherit()) { + command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); + } + } else { + command.addContextFilter(IContextFilter.INHERIT_PERMISSIONS); + } + + RequireParameters reqPar = method.getAnnotation(RequireParameters.class); + if (reqPar != null) { + list.setRequiredCount(reqPar.value() < 0 ? Integer.MAX_VALUE : reqPar.value()); + } else { + list.setRequiredCount(list.getIndexedParameters().size()); + } + + /* + PreprocessArgs preprocessArgs = method.getAnnotation(PreprocessArgs.class); + if (preprocessArgs != null) { + IArgumentPreProcessor preProcessor = IArgumentPreProcessor.mergeOnTokens(preprocessArgs.tokens(), preprocessArgs.escapeChar()); + list.setArgumentPreProcessor(preProcessor); + }*/ + + Desc desc = method.getAnnotation(Desc.class); + if (desc != null) { + String[] array = desc.value(); + if (array.length == 0) { + command.setDescription(desc.shortVersion()); + } else { + command.setDescription(array); + } + } else { + command.setDescription(); + } + + if (hasSenderParameter && Player.class.isAssignableFrom(senderParameterType)) { + command.addContextFilter(IContextFilter.PLAYER_ONLY); + } else if (hasSenderParameter && ConsoleCommandSender.class.isAssignableFrom(senderParameterType)) { + command.addContextFilter(IContextFilter.CONSOLE_ONLY); + } else if (method.isAnnotationPresent(RequirePlayer.class)) { + command.addContextFilter(IContextFilter.PLAYER_ONLY); + } else if (method.isAnnotationPresent(RequireConsole.class)) { + command.addContextFilter(IContextFilter.CONSOLE_ONLY); + } + + list.setRepeatFinalParameter(parameters.length > start && parameters[parameters.length - 1].isVarArgs()); + 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; + } + + public static int parseCommandAttributes(IParameterTypeSelector selector, Method method, ReflectiveCommand command) throws CommandParseException { + return parseCommandAttributes(selector, method, command, method.getParameters()); + } + + public static Parameter parseParameter(IParameterTypeSelector selector, Method method, java.lang.reflect.Parameter parameter, String name) throws CommandParseException { + Class type = parameter.getType(); + if (parameter.isVarArgs()) { + type = type.getComponentType(); + } + + Annotation[] annotations = parameter.getAnnotations(); + Flag flag = null; + Annotation typeAnnotation = null; + Desc desc = null; + + for (Annotation annotation : annotations) { + //noinspection StatementWithEmptyBody + if (annotation instanceof NamedArg) { + // do nothing + } else if (annotation instanceof Flag) { + if (flag != null) { + throw new CommandParseException("Multiple flags for the same parameter"); + } + flag = (Flag) annotation; + } else if (annotation instanceof Desc) { + if (desc != null) { + throw new CommandParseException("Multiple descriptions for the same parameter"); + } + desc = (Desc) annotation; + } else { + if (typeAnnotation != null) { + throw new CommandParseException("Multiple parameter type annotations for the same parameter"); + } + typeAnnotation = annotation; + } + } + + if (flag == null && name.startsWith("-")) { + throw new CommandParseException("Non-flag parameter's name starts with -"); + } else if (flag != null && !name.startsWith("-")) { + throw new CommandParseException("Flag parameter's name doesn't start with -"); + } + + ParameterType parameterType = selector.selectAny(type, typeAnnotation == null ? null : typeAnnotation.getClass()); + if (parameterType == null) { + throw new CommandParseException("IParameter type not found for parameter " + name + " in method " + method.toString()); + } + + Object parameterInfo; + if (typeAnnotation == null) { + parameterInfo = null; + } else try { + parameterInfo = parameterType.getParameterConfig() == null ? null : parameterType.getParameterConfig().getParameterInfo(typeAnnotation); + } catch (Exception ex) { + throw new CommandParseException("Invalid parameter config", ex); + } + + String descString = desc == null ? null : CommandAnnotationUtils.getShortDescription(desc); + + try { + //noinspection unchecked + String flagPermission = flag == null || flag.permission().isEmpty() ? null : flag.permission(); + return new Parameter<>(name, descString, parameterType, parameterInfo, type.isPrimitive(), name.startsWith("-"), flagPermission); + } catch (Exception ex) { + throw new CommandParseException("Invalid parameter", ex); + } + } + + public static void generateCommands(ICommandAddress address, String[] input) { + for (String value : input) { + Consumer consumer = PredefinedCommand.getPredefinedCommandGenerator(value); + if (consumer == null) { + System.out.println("[Command Warning] generated command '" + value + "' could not be found"); + } else { + consumer.accept(address); + } + } + } + + /* + Desired format + + @Cmd({"tp", "tpto"}) + @RequirePermissions("teleport.self") + public (static) String|void onCommand(Player sender, Player target, @Flag("force", permission = "teleport.self.force") boolean force) { + Validate.isTrue(force || !hasTpToggledOff(target), "Target has teleportation disabled. Use -force to ignore"); + sender.teleport(target); + //return + } + + parser needs to: + - see the @Cmd and create a CommandTree for it + - see that it must be a Player executing the command + - add an indexed IParameter for a Player type + - add a flag parameter named force, that consumes no arguments. + - see that setting the force flag requires a permission + */ + + private static void setDescription(ICommandAddress address, String[] array, String shortVersion) { + if (!address.hasCommand()) { + return; + } + + if (array.length == 0) { + address.getCommand().setDescription(shortVersion); + } else { + address.getCommand().setDescription(array); + } + + } + +} diff --git a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt index 2ef6e39..1a6692b 100644 --- a/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt +++ b/dicore3/command/src/main/kotlin/io/dico/dicore/command/registration/reflect/KotlinReflectiveRegistration.kt @@ -1,66 +1,66 @@ -package io.dico.dicore.command.registration.reflect - -import io.dico.dicore.command.* -import kotlinx.coroutines.CoroutineStart.UNDISPATCHED -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import java.lang.reflect.Method -import java.util.concurrent.CancellationException -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.intrinsics.intercepted -import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn -import kotlin.reflect.jvm.kotlinFunction - -fun isSuspendFunction(method: Method): Boolean { - val func = method.kotlinFunction ?: return false - return func.isSuspend -} - -fun callAsCoroutine( - command: ReflectiveCommand, - factory: ICommandInterceptor, - context: ExecutionContext, - args: Array -): 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, - // 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. - val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) { - suspendCoroutineUninterceptedOrReturn { cont -> - command.method.invoke(command.instance, *args, cont.intercepted()) - } - } - - if (job.isCompleted) { - return job.getResult() - } - - job.invokeOnCompletion { - val chatHandler = context.address.chatHandler - try { - val result = job.getResult() - chatHandler.sendMessage(context.sender, EMessageType.RESULT, result) - } catch (ex: Throwable) { - chatHandler.handleException(context.sender, context, ex) - } - } - - return null -} - -@Throws(CommandException::class) -private fun Deferred.getResult(): String? { - getCompletionExceptionOrNull()?.let { ex -> - if (ex is CancellationException) { - System.err.println("An asynchronously dispatched command was cancelled unexpectedly") - ex.printStackTrace() - throw CommandException("The command was cancelled unexpectedly (see console)") - } - if (ex is Exception) return ReflectiveCommand.getResult(null, ex) - throw ex - } - return ReflectiveCommand.getResult(getCompleted(), null) -} +package io.dico.dicore.command.registration.reflect + +import io.dico.dicore.command.* +import kotlinx.coroutines.CoroutineStart.UNDISPATCHED +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import java.lang.reflect.Method +import java.util.concurrent.CancellationException +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.intrinsics.intercepted +import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn +import kotlin.reflect.jvm.kotlinFunction + +fun isSuspendFunction(method: Method): Boolean { + val func = method.kotlinFunction ?: return false + return func.isSuspend +} + +fun callAsCoroutine( + command: ReflectiveCommand, + factory: ICommandInterceptor, + context: ExecutionContext, + args: Array +): 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, + // 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. + val job = GlobalScope.async(context = coroutineContext, start = UNDISPATCHED) { + suspendCoroutineUninterceptedOrReturn { cont -> + command.method.invoke(command.instance, *args, cont.intercepted()) + } + } + + if (job.isCompleted) { + return job.getResult() + } + + job.invokeOnCompletion { + val chatHandler = context.address.chatHandler + try { + val result = job.getResult() + chatHandler.sendMessage(context.sender, EMessageType.RESULT, result) + } catch (ex: Throwable) { + chatHandler.handleException(context.sender, context, ex) + } + } + + return null +} + +@Throws(CommandException::class) +private fun Deferred.getResult(): String? { + getCompletionExceptionOrNull()?.let { ex -> + if (ex is CancellationException) { + System.err.println("An asynchronously dispatched command was cancelled unexpectedly") + ex.printStackTrace() + throw CommandException("The command was cancelled unexpectedly (see console)") + } + if (ex is Exception) return ReflectiveCommand.getResult(null, ex) + throw ex + } + return ReflectiveCommand.getResult(getCompleted(), null) +} diff --git a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java index 1cd4e23..c31b96f 100644 --- a/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java +++ b/dicore3/command/src/test/java/io/dico/dicore/command/example/ParameterInfoObjectExample.java @@ -1,73 +1,73 @@ -package io.dico.dicore.command.example; - -import io.dico.dicore.command.CommandBuilder; -import io.dico.dicore.command.CommandException; -import io.dico.dicore.command.Validate; -import io.dico.dicore.command.annotation.Cmd; -import io.dico.dicore.command.parameter.ArgumentBuffer; -import io.dico.dicore.command.parameter.Parameter; -import io.dico.dicore.command.parameter.type.ParameterConfig; -import io.dico.dicore.command.parameter.type.ParameterType; -import org.bukkit.command.CommandSender; - -public class ParameterInfoObjectExample { - - private @interface ParentPermission { - String value(); - } - - static class MyInfoObject { - public static final ParameterConfig config = new ParameterConfig() { - @Override - protected MyInfoObject toParameterInfo(ParentPermission annotation) { - return new MyInfoObject(annotation.value()); - } - }; - - private String permissionParent; - - MyInfoObject(String permissionParent) { - this.permissionParent = permissionParent; - } - - public String getPermissionParent() { - return permissionParent; - } - } - - static class MyParameterType extends ParameterType { - - public MyParameterType() { - super(String.class, MyInfoObject.config); - } - - @Override - public String parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { - String value = buffer.next(); - - MyInfoObject mio = parameter.getParamInfo(); - if (mio != null) { - String permission = mio.permissionParent + "." + value; - Validate.isAuthorized(sender, permission); - } - - return value; - } - } - - static class MyCommands { - - @Cmd("test") - Object cmdTest(@ParentPermission("test.permission") String value) { - return "You have permission to use the argument '" + value + "'!"; - } - - } - - static void main(String[] args) { - new CommandBuilder() - .addParameterType(false, new MyParameterType()) - .registerCommands(new MyCommands()); - } - -} +package io.dico.dicore.command.example; + +import io.dico.dicore.command.CommandBuilder; +import io.dico.dicore.command.CommandException; +import io.dico.dicore.command.Validate; +import io.dico.dicore.command.annotation.Cmd; +import io.dico.dicore.command.parameter.ArgumentBuffer; +import io.dico.dicore.command.parameter.Parameter; +import io.dico.dicore.command.parameter.type.ParameterConfig; +import io.dico.dicore.command.parameter.type.ParameterType; +import org.bukkit.command.CommandSender; + +public class ParameterInfoObjectExample { + + private @interface ParentPermission { + String value(); + } + + static class MyInfoObject { + public static final ParameterConfig config = new ParameterConfig() { + @Override + protected MyInfoObject toParameterInfo(ParentPermission annotation) { + return new MyInfoObject(annotation.value()); + } + }; + + private String permissionParent; + + MyInfoObject(String permissionParent) { + this.permissionParent = permissionParent; + } + + public String getPermissionParent() { + return permissionParent; + } + } + + static class MyParameterType extends ParameterType { + + public MyParameterType() { + super(String.class, MyInfoObject.config); + } + + @Override + public String parse(Parameter parameter, CommandSender sender, ArgumentBuffer buffer) throws CommandException { + String value = buffer.next(); + + MyInfoObject mio = parameter.getParamInfo(); + if (mio != null) { + String permission = mio.permissionParent + "." + value; + Validate.isAuthorized(sender, permission); + } + + return value; + } + } + + static class MyCommands { + + @Cmd("test") + Object cmdTest(@ParentPermission("test.permission") String value) { + return "You have permission to use the argument '" + value + "'!"; + } + + } + + static void main(String[] args) { + new CommandBuilder() + .addParameterType(false, new MyParameterType()) + .registerCommands(new MyCommands()); + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java index e4facb1..ae25123 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java +++ b/dicore3/core/src/main/java/io/dico/dicore/BitModifier.java @@ -1,182 +1,182 @@ -package io.dico.dicore; - -public interface BitModifier { - - long mask(); - - long get(long x); - - long set(long x, long value); - - default int lowerBound() { - return Long.numberOfTrailingZeros(mask()); - } - - default int upperBound() { - return 64 - Long.numberOfLeadingZeros(mask()); - } - - default int bitCount() { - return Long.bitCount(mask()); - } - - default boolean getBoolean(long x) { - return get(x) == 1; - } - - default long setBoolean(long x, boolean value) { - return set(x, value ? 1 : 0); - } - - default int getInt(long x) { - return (int) (get(x) & 0xFFFFFFFFL); - } - - default long setInt(long x, int value) { - return set(x, value & 0xFFFFFFFFL); - } - - default short getShort(long x) { - return (short) (get(x) & 0xFFFFL); - } - - default long setShort(long x, int value) { - return set(x, value & 0xFFFFL); - } - - default byte getByte(long x) { - return (byte) (get(x) & 0xFFL); - } - - default long setByte(long x, int value) { - return set(x, value & 0xFFL); - } - - final class OfSingle implements BitModifier { - private final long mask; - - public OfSingle(int bit) { - if (bit < 0 || bit >= 64) { - throw new IndexOutOfBoundsException(); - } - this.mask = 1L << bit; - } - - @Override - public int bitCount() { - return 1; - } - - @Override - public long mask() { - return mask; - } - - public boolean getBoolean(long x) { - return (x & mask) != 0; - } - - public long setBoolean(long x, boolean value) { - return value ? (x | mask) : (x & ~mask); - } - - @Override - public long get(long x) { - return getBoolean(x) ? 1 : 0; - } - - @Override - public long set(long x, long value) { - if (value < 0 || value > 1) { - throw new IllegalArgumentException(); - } - return setBoolean(x, value == 1); - } - } - - final class OfMultiple implements BitModifier { - private final int lowerBound; - private final int bitCount; - private final long mask; - - public OfMultiple(int lowerBound, int bitCount) { - int upperBound = lowerBound + bitCount; - if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) { - throw new IndexOutOfBoundsException(); - } - this.lowerBound = lowerBound; - this.bitCount = bitCount; - this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound); - } - - @Override - public int lowerBound() { - return lowerBound; - } - - @Override - public int bitCount() { - return bitCount; - } - - @Override - public int upperBound() { - return lowerBound + bitCount; - } - - @Override - public long mask() { - return mask; - } - - @Override - public long get(long x) { - return (x & mask) >>> lowerBound; - } - - @Override - public long set(long x, long value) { - return (x & ~mask) | ((value << lowerBound) & mask); - } - - } - - class Builder { - int currentIndex; - - public int getCurrentIndex() { - return currentIndex; - } - - public int getRemaining() { - return 64 - currentIndex; - } - - public OfSingle single() { - checkAvailable(1); - return new OfSingle(currentIndex++); - } - - public BitModifier size(int size) { - if (size == 1) { - return single(); - } - checkAvailable(size); - BitModifier result = new OfMultiple(currentIndex, size); - currentIndex += size; - return result; - } - - public BitModifier remaining() { - return size(getRemaining()); - } - - private void checkAvailable(int size) { - if (size <= 0 || currentIndex + size > 64) { - throw new IllegalStateException("Exceeding bit count of a long"); - } - } - - } - -} +package io.dico.dicore; + +public interface BitModifier { + + long mask(); + + long get(long x); + + long set(long x, long value); + + default int lowerBound() { + return Long.numberOfTrailingZeros(mask()); + } + + default int upperBound() { + return 64 - Long.numberOfLeadingZeros(mask()); + } + + default int bitCount() { + return Long.bitCount(mask()); + } + + default boolean getBoolean(long x) { + return get(x) == 1; + } + + default long setBoolean(long x, boolean value) { + return set(x, value ? 1 : 0); + } + + default int getInt(long x) { + return (int) (get(x) & 0xFFFFFFFFL); + } + + default long setInt(long x, int value) { + return set(x, value & 0xFFFFFFFFL); + } + + default short getShort(long x) { + return (short) (get(x) & 0xFFFFL); + } + + default long setShort(long x, int value) { + return set(x, value & 0xFFFFL); + } + + default byte getByte(long x) { + return (byte) (get(x) & 0xFFL); + } + + default long setByte(long x, int value) { + return set(x, value & 0xFFL); + } + + final class OfSingle implements BitModifier { + private final long mask; + + public OfSingle(int bit) { + if (bit < 0 || bit >= 64) { + throw new IndexOutOfBoundsException(); + } + this.mask = 1L << bit; + } + + @Override + public int bitCount() { + return 1; + } + + @Override + public long mask() { + return mask; + } + + public boolean getBoolean(long x) { + return (x & mask) != 0; + } + + public long setBoolean(long x, boolean value) { + return value ? (x | mask) : (x & ~mask); + } + + @Override + public long get(long x) { + return getBoolean(x) ? 1 : 0; + } + + @Override + public long set(long x, long value) { + if (value < 0 || value > 1) { + throw new IllegalArgumentException(); + } + return setBoolean(x, value == 1); + } + } + + final class OfMultiple implements BitModifier { + private final int lowerBound; + private final int bitCount; + private final long mask; + + public OfMultiple(int lowerBound, int bitCount) { + int upperBound = lowerBound + bitCount; + if (lowerBound < 0 || lowerBound >= 64 || upperBound < 1 || upperBound > 64 || upperBound < lowerBound) { + throw new IndexOutOfBoundsException(); + } + this.lowerBound = lowerBound; + this.bitCount = bitCount; + this.mask = (Long.MIN_VALUE >> (bitCount - 1)) >>> (64 - bitCount - lowerBound); + } + + @Override + public int lowerBound() { + return lowerBound; + } + + @Override + public int bitCount() { + return bitCount; + } + + @Override + public int upperBound() { + return lowerBound + bitCount; + } + + @Override + public long mask() { + return mask; + } + + @Override + public long get(long x) { + return (x & mask) >>> lowerBound; + } + + @Override + public long set(long x, long value) { + return (x & ~mask) | ((value << lowerBound) & mask); + } + + } + + class Builder { + int currentIndex; + + public int getCurrentIndex() { + return currentIndex; + } + + public int getRemaining() { + return 64 - currentIndex; + } + + public OfSingle single() { + checkAvailable(1); + return new OfSingle(currentIndex++); + } + + public BitModifier size(int size) { + if (size == 1) { + return single(); + } + checkAvailable(size); + BitModifier result = new OfMultiple(currentIndex, size); + currentIndex += size; + return result; + } + + public BitModifier remaining() { + return size(getRemaining()); + } + + private void checkAvailable(int size) { + if (size <= 0 || currentIndex + size > 64) { + throw new IllegalStateException("Exceeding bit count of a long"); + } + } + + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java index 9fdfe0e..40afb9b 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Formatting.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Formatting.java @@ -1,295 +1,295 @@ -/* - * Copyright (c) 2017 ProjectOreville - * - * All rights reserved. - * - * Author(s): - * Dico Karssiens - */ - -package io.dico.dicore; - -import org.jetbrains.annotations.NotNull; - -public final class Formatting implements CharSequence { - public static final char FORMAT_CHAR = '\u00a7'; - private static final String CACHED_CHARS = "0123456789abcdefklmnor"; - private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()]; - - @NotNull - public static final Formatting - BLACK = from('0'), - DARK_BLUE = from('1'), - DARL_GREEN = from('2'), - CYAN = from('3'), - DARK_RED = from('4'), - PURPLE = from('5'), - ORANGE = from('6'), - GRAY = from('7'), - DARK_GRAY = from('8'), - BLUE = from('9'), - GREEN = from('a'), - AQUA = from('b'), - RED = from('c'), - PINK = from('d'), - YELLOW = from('e'), - WHITE = from('f'), - BOLD = from('l'), - STRIKETHROUGH = from('m'), - UNDERLINE = from('n'), - ITALIC = from('o'), - MAGIC = from('k'), - RESET = from('r'), - EMPTY = from('\0'); - - public static String stripAll(String value) { - return stripAll(FORMAT_CHAR, value); - } - - public static String stripAll(char alternateChar, String value) { - int index = value.indexOf(alternateChar); - int max; - if (index == -1 || index == (max = value.length() - 1)) { - return value; - } - - StringBuilder result = new StringBuilder(); - int from = 0; - do { - if (isRecognizedChar(value.charAt(index + 1))) { - result.append(value, from, index); - from = index + 2; - } else { - result.append(value, from, from = index + 2); - } - - index = value.indexOf(alternateChar, index + 1); - } while (index != -1 && index != max && from <= max); - - if (from <= max) { - result.append(value, from, value.length()); - } - return result.toString(); - } - - public static String stripFirst(String value) { - return stripFirst(FORMAT_CHAR, value); - } - - public static String stripFirst(char alternateChar, String value) { - int index = value.indexOf(alternateChar); - int max; - if (index == -1 || index == (max = value.length() - 1)) { - return value; - } - - StringBuilder result = new StringBuilder(value.length()); - int from = 0; - if (isRecognizedChar(value.charAt(index + 1))) { - result.append(value, from, index); - from = index + 2; - } else { - result.append(value, from, from = index + 2); - } - - if (from < max) { - result.append(value, from, value.length()); - } - return result.toString(); - } - - public static Formatting from(char c) { - if (isRecognizedChar(c)) { - c = Character.toLowerCase(c); - int index = CACHED_CHARS.indexOf(c); - if (index == -1) return EMPTY; - - Formatting res = singleCharInstances[index]; - if (res == null) { - singleCharInstances[index] = res = new Formatting(c); - } - return res; - } - return EMPTY; - } - - public static Formatting from(String chars) { - return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0'); - } - - public static Formatting getFormats(String input) { - return getFormats(input, FORMAT_CHAR); - } - - public static Formatting getFormats(String input, char formatChar) { - return getFormats(input, 0, input.length(), formatChar); - } - - public static Formatting getFormats(String input, int start, int end, char formatChar) { - if ((start < 0) || (start > end) || (end > input.length())) { - throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length()); - } - - boolean needsFormatChar = formatChar != '\0'; - char[] formats = new char[6]; - // just make sure it's not the same as formatChar - char previous = (char) (formatChar + 1); - - for (int i = start; i < end; i++) { - char c = input.charAt(i); - - if (previous == formatChar || !needsFormatChar) { - if (isColourChar(c) || isResetChar(c)) { - formats = new char[6]; - formats[0] = Character.toLowerCase(c); - } else if (isFormatChar(c)) { - char format = Character.toLowerCase(c); - for (int j = 0; j < 6; j++) { - if (formats[j] == '\0') { - formats[j] = format; - break; - } else if (formats[j] == format) { - break; - } - } - } - } - - previous = c; - } - - return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats); - } - - public static String translate(String input) { - return translateChars('&', input); - } - - public static String translateChars(char alternateChar, String input) { - return translateFormat(alternateChar, FORMAT_CHAR, input); - } - - public static String revert(String input) { - return revertChars('&', input); - } - - public static String revertChars(char alternateChar, String input) { - return translateFormat(FORMAT_CHAR, alternateChar, input); - } - - public static String translateFormat(char fromChar, char toChar, String input) { - if (input == null) { - return null; - } - int n = input.length(); - if (n < 2) { - return input; - } - char[] result = null; - char previous = input.charAt(0); - for (int i = 1; i < n; i++) { - char c = input.charAt(i); - if (previous == fromChar && isRecognizedChar(c)) { - if (result == null) { - result = input.toCharArray(); - } - result[i - 1] = toChar; - } - previous = c; - } - return result == null ? input : String.valueOf(result); - } - - public static void translate(StringBuilder input) { - translateChars('&', input); - } - - public static void translateChars(char alternateChar, StringBuilder input) { - translateFormat(alternateChar, FORMAT_CHAR, input); - } - - public static void revert(StringBuilder input) { - revertChars('&', input); - } - - public static void revertChars(char alternateChar, StringBuilder input) { - translateFormat(FORMAT_CHAR, alternateChar, input); - } - - public static void translateFormat(char fromChar, char toChar, StringBuilder input) { - if (input == null) { - return; - } - int n = input.length(); - if (n < 2) { - return; - } - char previous = input.charAt(0); - for (int i = 1; i < n; i++) { - char c = input.charAt(i); - if (previous == fromChar && isRecognizedChar(c)) { - input.setCharAt(i -1, toChar); - } - previous = c; - } - } - - private static boolean isRecognizedChar(char c) { - return isColourChar(c) || isFormatChar(c) || isResetChar(c); - } - - private static boolean isColourChar(char c) { - return "0123456789abcdefABCDEF".indexOf(c) >= 0; - } - - private static boolean isResetChar(char c) { - return c == 'r' || c == 'R'; - } - - private static boolean isFormatChar(char c) { - return "klmnoKLMNO".indexOf(c) >= 0; - } - - private final String format; - - private Formatting(char[] formats) { - StringBuilder format = new StringBuilder(12); - for (char c : formats) { - if (c != '\0') { - format.append(FORMAT_CHAR).append(c); - } else { - break; - } - } - this.format = format.toString(); - } - - private Formatting(char c) { - this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : ""; - } - - @Override - public int length() { - return format.length(); - } - - @Override - public char charAt(int index) { - return format.charAt(index); - } - - @Override - public String subSequence(int start, int end) { - return format.substring(start, end); - } - - @Override - public String toString() { - return format; - } - - public String toString(char formatChar) { - return format.replace(FORMAT_CHAR, formatChar); - } - +/* + * Copyright (c) 2017 ProjectOreville + * + * All rights reserved. + * + * Author(s): + * Dico Karssiens + */ + +package io.dico.dicore; + +import org.jetbrains.annotations.NotNull; + +public final class Formatting implements CharSequence { + public static final char FORMAT_CHAR = '\u00a7'; + private static final String CACHED_CHARS = "0123456789abcdefklmnor"; + private static final Formatting[] singleCharInstances = new Formatting[CACHED_CHARS.length()]; + + @NotNull + public static final Formatting + BLACK = from('0'), + DARK_BLUE = from('1'), + DARL_GREEN = from('2'), + CYAN = from('3'), + DARK_RED = from('4'), + PURPLE = from('5'), + ORANGE = from('6'), + GRAY = from('7'), + DARK_GRAY = from('8'), + BLUE = from('9'), + GREEN = from('a'), + AQUA = from('b'), + RED = from('c'), + PINK = from('d'), + YELLOW = from('e'), + WHITE = from('f'), + BOLD = from('l'), + STRIKETHROUGH = from('m'), + UNDERLINE = from('n'), + ITALIC = from('o'), + MAGIC = from('k'), + RESET = from('r'), + EMPTY = from('\0'); + + public static String stripAll(String value) { + return stripAll(FORMAT_CHAR, value); + } + + public static String stripAll(char alternateChar, String value) { + int index = value.indexOf(alternateChar); + int max; + if (index == -1 || index == (max = value.length() - 1)) { + return value; + } + + StringBuilder result = new StringBuilder(); + int from = 0; + do { + if (isRecognizedChar(value.charAt(index + 1))) { + result.append(value, from, index); + from = index + 2; + } else { + result.append(value, from, from = index + 2); + } + + index = value.indexOf(alternateChar, index + 1); + } while (index != -1 && index != max && from <= max); + + if (from <= max) { + result.append(value, from, value.length()); + } + return result.toString(); + } + + public static String stripFirst(String value) { + return stripFirst(FORMAT_CHAR, value); + } + + public static String stripFirst(char alternateChar, String value) { + int index = value.indexOf(alternateChar); + int max; + if (index == -1 || index == (max = value.length() - 1)) { + return value; + } + + StringBuilder result = new StringBuilder(value.length()); + int from = 0; + if (isRecognizedChar(value.charAt(index + 1))) { + result.append(value, from, index); + from = index + 2; + } else { + result.append(value, from, from = index + 2); + } + + if (from < max) { + result.append(value, from, value.length()); + } + return result.toString(); + } + + public static Formatting from(char c) { + if (isRecognizedChar(c)) { + c = Character.toLowerCase(c); + int index = CACHED_CHARS.indexOf(c); + if (index == -1) return EMPTY; + + Formatting res = singleCharInstances[index]; + if (res == null) { + singleCharInstances[index] = res = new Formatting(c); + } + return res; + } + return EMPTY; + } + + public static Formatting from(String chars) { + return chars.length() == 1 ? from(chars.charAt(0)) : getFormats(chars, '\0'); + } + + public static Formatting getFormats(String input) { + return getFormats(input, FORMAT_CHAR); + } + + public static Formatting getFormats(String input, char formatChar) { + return getFormats(input, 0, input.length(), formatChar); + } + + public static Formatting getFormats(String input, int start, int end, char formatChar) { + if ((start < 0) || (start > end) || (end > input.length())) { + throw new IndexOutOfBoundsException("start " + start + ", end " + end + ", input.length() " + input.length()); + } + + boolean needsFormatChar = formatChar != '\0'; + char[] formats = new char[6]; + // just make sure it's not the same as formatChar + char previous = (char) (formatChar + 1); + + for (int i = start; i < end; i++) { + char c = input.charAt(i); + + if (previous == formatChar || !needsFormatChar) { + if (isColourChar(c) || isResetChar(c)) { + formats = new char[6]; + formats[0] = Character.toLowerCase(c); + } else if (isFormatChar(c)) { + char format = Character.toLowerCase(c); + for (int j = 0; j < 6; j++) { + if (formats[j] == '\0') { + formats[j] = format; + break; + } else if (formats[j] == format) { + break; + } + } + } + } + + previous = c; + } + + return formats[1] == '\0' ? from(formats[0]) : new Formatting(formats); + } + + public static String translate(String input) { + return translateChars('&', input); + } + + public static String translateChars(char alternateChar, String input) { + return translateFormat(alternateChar, FORMAT_CHAR, input); + } + + public static String revert(String input) { + return revertChars('&', input); + } + + public static String revertChars(char alternateChar, String input) { + return translateFormat(FORMAT_CHAR, alternateChar, input); + } + + public static String translateFormat(char fromChar, char toChar, String input) { + if (input == null) { + return null; + } + int n = input.length(); + if (n < 2) { + return input; + } + char[] result = null; + char previous = input.charAt(0); + for (int i = 1; i < n; i++) { + char c = input.charAt(i); + if (previous == fromChar && isRecognizedChar(c)) { + if (result == null) { + result = input.toCharArray(); + } + result[i - 1] = toChar; + } + previous = c; + } + return result == null ? input : String.valueOf(result); + } + + public static void translate(StringBuilder input) { + translateChars('&', input); + } + + public static void translateChars(char alternateChar, StringBuilder input) { + translateFormat(alternateChar, FORMAT_CHAR, input); + } + + public static void revert(StringBuilder input) { + revertChars('&', input); + } + + public static void revertChars(char alternateChar, StringBuilder input) { + translateFormat(FORMAT_CHAR, alternateChar, input); + } + + public static void translateFormat(char fromChar, char toChar, StringBuilder input) { + if (input == null) { + return; + } + int n = input.length(); + if (n < 2) { + return; + } + char previous = input.charAt(0); + for (int i = 1; i < n; i++) { + char c = input.charAt(i); + if (previous == fromChar && isRecognizedChar(c)) { + input.setCharAt(i -1, toChar); + } + previous = c; + } + } + + private static boolean isRecognizedChar(char c) { + return isColourChar(c) || isFormatChar(c) || isResetChar(c); + } + + private static boolean isColourChar(char c) { + return "0123456789abcdefABCDEF".indexOf(c) >= 0; + } + + private static boolean isResetChar(char c) { + return c == 'r' || c == 'R'; + } + + private static boolean isFormatChar(char c) { + return "klmnoKLMNO".indexOf(c) >= 0; + } + + private final String format; + + private Formatting(char[] formats) { + StringBuilder format = new StringBuilder(12); + for (char c : formats) { + if (c != '\0') { + format.append(FORMAT_CHAR).append(c); + } else { + break; + } + } + this.format = format.toString(); + } + + private Formatting(char c) { + this.format = (c != '\0') ? String.valueOf(new char[]{FORMAT_CHAR, c}) : ""; + } + + @Override + public int length() { + return format.length(); + } + + @Override + public char charAt(int index) { + return format.charAt(index); + } + + @Override + public String subSequence(int start, int end) { + return format.substring(start, end); + } + + @Override + public String toString() { + return format; + } + + public String toString(char formatChar) { + return format.replace(FORMAT_CHAR, formatChar); + } + } \ No newline at end of file diff --git a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java index b59979a..f92da6b 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java +++ b/dicore3/core/src/main/java/io/dico/dicore/InterfaceChain.java @@ -1,173 +1,173 @@ -package io.dico.dicore; - -/** - * A chainable object - *

- * 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. - * - * @param the interface that is chainable - * @param the element of the chain, this is a supertype of the subtype - */ -@SuppressWarnings("unchecked") -public interface InterfaceChain> { - - /** - * returns the empty InterfaceChain instance. - * - * @return the empty InterfaceChain instance - */ - Subtype getEmptyInstance(); - - /** - * returns a InterfaceChain with the last added element detached. - *

- * if this InterfaceChain is the empty instance, the empty instance is returned. - * - * @return a InterfaceChain with the last added element detached - * @implNote for the purpose of lambdas, the default implementation also returns the empty instance. - */ - default Subtype withoutLastNode() { - return getEmptyInstance(); - } - - /** - * returns the element that was inserted as the last element - *

- * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)} - * would return the given element. - * - * @return the element that was inserted as the last element - * @implNote for the purpose of lambdas, the default implementation returns this object, - * which is required to implement the Element type parameter. - */ - default Element getDelegateOfLastNode() { - //noinspection unchecked - return (Element) this; - } - - /** - * @return The number of elements chained from this InterfaceChain. - * @implNote for the purpose of lambdas, the default implementation returns 1. - */ - default int getElementCount() { - return 1; - } - - /** - * Get a new InterfaceChain that includes the given Element. - *

- * The default implementation of the Subtype should look like this: - *

 {@code
-     * if (element == null) {
-     *     return this;
-     * }
-     *
-     * int count = getElementCount() + 1;
-     * return new Subtype() {
-     *     \@Override
-     *     public void exampleElementMethod() {
-     *         try {
-     *             Subtype.this.exampleElementMethod();
-     *         } finally {
-     *             element.exampleElementMethod();
-     *         }
-     *     }
-     *
-     *     \@Override
-     *     public Subtype withoutLastNode() {
-     *         return Subtype.this;
-     *     }
-     *
-     *     \@Override
-     *     public Element getDelegateOfLastNode() {
-     *         return element;
-     *     }
-     *
-     *     \@Override
-     *     public int getElementCount() {
-     *         return count;
-     *     }
-     * };
-     * }
-     * 
- * - * @param element A new element to insert at the end of this InterfaceChain. - * @return a new InterfaceChain that includes the given Element. - */ - Subtype withElement(Element element); - - /** - * Append each of the elements to this InterfaceChain - * - * @param elements the elements to append - * @return a new InterfaceChain with the elements appended - */ - default Subtype withElements(Element... elements) { - Subtype result = (Subtype) this; - for (Element element : elements) { - result = result.withElement(element); - } - return result; - } - - /* - Example Subtypes implementation - - public class Subtypes { - - private Subtypes() { - - } - - private static final Subtype empty = new Subtype() { - @Override - public void exampleElementMethod() { - - } - - @Override - public Subtype withElement(Element other) { - return Subtypes.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public Element getDelegateOfLastNode() { - return null; - } - }; - - public static Subtype empty() { - return empty; - } - - public static Subtype singleton(Element element) { - if (element instanceof Subtype) { - return (Subtype) element; - } - if (element == null) { - return empty(); - } - return new Subtype() { - @Override - public void exampleElementMethod() { - element.exampleElementMethod(); - } - - @Override - public Element getDelegateOfLastNode() { - return element; - } - }; - } - - } - - */ - -} +package io.dico.dicore; + +/** + * A chainable object + *

+ * 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. + * + * @param the interface that is chainable + * @param the element of the chain, this is a supertype of the subtype + */ +@SuppressWarnings("unchecked") +public interface InterfaceChain> { + + /** + * returns the empty InterfaceChain instance. + * + * @return the empty InterfaceChain instance + */ + Subtype getEmptyInstance(); + + /** + * returns a InterfaceChain with the last added element detached. + *

+ * if this InterfaceChain is the empty instance, the empty instance is returned. + * + * @return a InterfaceChain with the last added element detached + * @implNote for the purpose of lambdas, the default implementation also returns the empty instance. + */ + default Subtype withoutLastNode() { + return getEmptyInstance(); + } + + /** + * returns the element that was inserted as the last element + *

+ * For instance, calling this method on the result of calling {@link InterfaceChain#withElement(Object)} + * would return the given element. + * + * @return the element that was inserted as the last element + * @implNote for the purpose of lambdas, the default implementation returns this object, + * which is required to implement the Element type parameter. + */ + default Element getDelegateOfLastNode() { + //noinspection unchecked + return (Element) this; + } + + /** + * @return The number of elements chained from this InterfaceChain. + * @implNote for the purpose of lambdas, the default implementation returns 1. + */ + default int getElementCount() { + return 1; + } + + /** + * Get a new InterfaceChain that includes the given Element. + *

+ * The default implementation of the Subtype should look like this: + *

 {@code
+     * if (element == null) {
+     *     return this;
+     * }
+     *
+     * int count = getElementCount() + 1;
+     * return new Subtype() {
+     *     \@Override
+     *     public void exampleElementMethod() {
+     *         try {
+     *             Subtype.this.exampleElementMethod();
+     *         } finally {
+     *             element.exampleElementMethod();
+     *         }
+     *     }
+     *
+     *     \@Override
+     *     public Subtype withoutLastNode() {
+     *         return Subtype.this;
+     *     }
+     *
+     *     \@Override
+     *     public Element getDelegateOfLastNode() {
+     *         return element;
+     *     }
+     *
+     *     \@Override
+     *     public int getElementCount() {
+     *         return count;
+     *     }
+     * };
+     * }
+     * 
+ * + * @param element A new element to insert at the end of this InterfaceChain. + * @return a new InterfaceChain that includes the given Element. + */ + Subtype withElement(Element element); + + /** + * Append each of the elements to this InterfaceChain + * + * @param elements the elements to append + * @return a new InterfaceChain with the elements appended + */ + default Subtype withElements(Element... elements) { + Subtype result = (Subtype) this; + for (Element element : elements) { + result = result.withElement(element); + } + return result; + } + + /* + Example Subtypes implementation + + public class Subtypes { + + private Subtypes() { + + } + + private static final Subtype empty = new Subtype() { + @Override + public void exampleElementMethod() { + + } + + @Override + public Subtype withElement(Element other) { + return Subtypes.singleton(other); + } + + @Override + public int getElementCount() { + return 0; + } + + @Override + public Element getDelegateOfLastNode() { + return null; + } + }; + + public static Subtype empty() { + return empty; + } + + public static Subtype singleton(Element element) { + if (element instanceof Subtype) { + return (Subtype) element; + } + if (element == null) { + return empty(); + } + return new Subtype() { + @Override + public void exampleElementMethod() { + element.exampleElementMethod(); + } + + @Override + public Element getDelegateOfLastNode() { + return element; + } + }; + } + + } + + */ + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java index 5c19848..3be2179 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/InventoryEventUtil.java @@ -1,120 +1,120 @@ -package io.dico.dicore; - -import org.bukkit.Material; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.PlayerInventory; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -public class InventoryEventUtil { - - private InventoryEventUtil() { - - } - - public static ItemStack getNewItem(InventoryClickEvent event) { - Inventory clicked = event.getInventory(); - switch (event.getAction()) { - case SWAP_WITH_CURSOR: - case PLACE_ALL: - return event.getCursor(); - case PICKUP_ALL: - case HOTBAR_MOVE_AND_READD: - case MOVE_TO_OTHER_INVENTORY: - case DROP_ALL_SLOT: - case COLLECT_TO_CURSOR: - return null; - case PICKUP_HALF: - case PICKUP_SOME: - ItemStack item = clicked.getItem(event.getSlot()).clone(); - item.setAmount(item.getAmount() / 2); - return item; - case PICKUP_ONE: - case DROP_ONE_SLOT: - item = clicked.getItem(event.getSlot()).clone(); - item.setAmount(Math.max(0, item.getAmount() - 1)); - return item; - case PLACE_ONE: - item = event.getView().getCursor().clone(); - item.setAmount(1); - return item; - case PLACE_SOME: - item = event.getView().getCursor().clone(); - item.setAmount(item.getAmount() / 2); - return item; - case HOTBAR_SWAP: - return event.getView().getBottomInventory().getItem(event.getHotbarButton()); - default: - return clicked.getItem(event.getSlot()); - } - } - - - public static Map deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) { - int addedAmount = added.getAmount(); - Map rv = Collections.emptyMap(); - - for (int n = inventory.getSize(), i = 0; i < n; i++) { - if (addedAmount <= 0) break; - - ItemStack current = inventory.getItem(i); - if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) { - int count = current == null ? 0 : current.getAmount(); - int max = (current == null ? added : current).getType().getMaxStackSize(); - if (count < max) { - int diff = max - count; - if (diff > addedAmount) { - diff = addedAmount; - } - addedAmount -= diff; - - if (rv.isEmpty()) rv = new LinkedHashMap<>(); - - if (computeNewItem) { - current = (current == null ? added : current).clone(); - current.setAmount(count + diff); - rv.put(i, current); - } else { - rv.put(i, null); - } - } - } - } - - return rv; - } - - - public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) { - int heldItemSlot = inventory.getHeldItemSlot(); - ItemStack heldItem = inventory.getItem(heldItemSlot); - - if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) { - return null; - } - - int amt = added.getAmount(); - for (int i = 0; i < heldItemSlot; i++) { - ItemStack item = inventory.getItem(i); - if (!SpigotUtil.isItemPresent(item)) { - return null; - } - if (item.isSimilar(item)) { - amt -= Math.max(0, item.getMaxStackSize() - item.getAmount()); - if (amt <= 0) { - return null; - } - } - } - - added = added.clone(); - added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount())); - return added; - } - - -} +package io.dico.dicore; + +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public class InventoryEventUtil { + + private InventoryEventUtil() { + + } + + public static ItemStack getNewItem(InventoryClickEvent event) { + Inventory clicked = event.getInventory(); + switch (event.getAction()) { + case SWAP_WITH_CURSOR: + case PLACE_ALL: + return event.getCursor(); + case PICKUP_ALL: + case HOTBAR_MOVE_AND_READD: + case MOVE_TO_OTHER_INVENTORY: + case DROP_ALL_SLOT: + case COLLECT_TO_CURSOR: + return null; + case PICKUP_HALF: + case PICKUP_SOME: + ItemStack item = clicked.getItem(event.getSlot()).clone(); + item.setAmount(item.getAmount() / 2); + return item; + case PICKUP_ONE: + case DROP_ONE_SLOT: + item = clicked.getItem(event.getSlot()).clone(); + item.setAmount(Math.max(0, item.getAmount() - 1)); + return item; + case PLACE_ONE: + item = event.getView().getCursor().clone(); + item.setAmount(1); + return item; + case PLACE_SOME: + item = event.getView().getCursor().clone(); + item.setAmount(item.getAmount() / 2); + return item; + case HOTBAR_SWAP: + return event.getView().getBottomInventory().getItem(event.getHotbarButton()); + default: + return clicked.getItem(event.getSlot()); + } + } + + + public static Map deduceChangesIfItemAdded(Inventory inventory, ItemStack added, boolean computeNewItem) { + int addedAmount = added.getAmount(); + Map rv = Collections.emptyMap(); + + for (int n = inventory.getSize(), i = 0; i < n; i++) { + if (addedAmount <= 0) break; + + ItemStack current = inventory.getItem(i); + if (current == null || current.getType() == Material.AIR || current.isSimilar(added)) { + int count = current == null ? 0 : current.getAmount(); + int max = (current == null ? added : current).getType().getMaxStackSize(); + if (count < max) { + int diff = max - count; + if (diff > addedAmount) { + diff = addedAmount; + } + addedAmount -= diff; + + if (rv.isEmpty()) rv = new LinkedHashMap<>(); + + if (computeNewItem) { + current = (current == null ? added : current).clone(); + current.setAmount(count + diff); + rv.put(i, current); + } else { + rv.put(i, null); + } + } + } + } + + return rv; + } + + + public static ItemStack getNewHeldItemIfPickedUp(PlayerInventory inventory, ItemStack added) { + int heldItemSlot = inventory.getHeldItemSlot(); + ItemStack heldItem = inventory.getItem(heldItemSlot); + + if (SpigotUtil.isItemPresent(heldItem) && !added.isSimilar(heldItem)) { + return null; + } + + int amt = added.getAmount(); + for (int i = 0; i < heldItemSlot; i++) { + ItemStack item = inventory.getItem(i); + if (!SpigotUtil.isItemPresent(item)) { + return null; + } + if (item.isSimilar(item)) { + amt -= Math.max(0, item.getMaxStackSize() - item.getAmount()); + if (amt <= 0) { + return null; + } + } + } + + added = added.clone(); + added.setAmount(amt + Math.max(0, heldItem == null ? 0 : heldItem.getAmount())); + return added; + } + + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/Logging.java b/dicore3/core/src/main/java/io/dico/dicore/Logging.java index fa2a676..50c4b52 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Logging.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Logging.java @@ -1,116 +1,116 @@ -package io.dico.dicore; - -import java.util.logging.Logger; - -public interface Logging { - - void info(Object o); - - void warn(Object o); - - void error(Object o); - - void debug(Object o); - - void setDebugging(boolean debugging); - - boolean isDebugging(); - - class RootLogging implements Logging { - private final String prefix; - private final Logger root; - private boolean debugging; - - public RootLogging(String prefix, Logger root, boolean debugging) { - this.root = root; - this.prefix = prefix; - this.debugging = debugging; - } - - @Override - public void info(Object o) { - root.info(prefix(o)); - } - - @Override - public void warn(Object o) { - root.warning(prefix(o)); - } - - @Override - public void error(Object o) { - root.severe(prefix(o)); - } - - @Override - public void debug(Object o) { - if (debugging) { - root.info(String.format("[DEBUG] %s", prefix(o))); - } - } - - @Override - public boolean isDebugging() { - return debugging; - } - - @Override - public void setDebugging(boolean debugging) { - this.debugging = debugging; - } - - private String prefix(Object o) { - return String.format("[%s] %s", prefix, String.valueOf(o)); - } - } - - class SubLogging implements Logging { - protected String prefix; - private final Logging superLogger; - private boolean debugging; - - public SubLogging(String prefix, Logging superLogger, boolean debugging) { - this.superLogger = superLogger; - this.prefix = prefix; - this.debugging = debugging; - } - - @Override - public void info(Object o) { - superLogger.info(prefix(o)); - } - - @Override - public void warn(Object o) { - superLogger.warn(prefix(o)); - } - - @Override - public void error(Object o) { - superLogger.error(prefix(o)); - } - - @Override - public void debug(Object o) { - if (debugging) { - superLogger.info(String.format("[DEBUG] %s", prefix(o))); - } - } - - @Override - public boolean isDebugging() { - return debugging; - } - - @Override - public void setDebugging(boolean debugging) { - this.debugging = debugging; - } - - private String prefix(Object o) { - return String.format("[%s] %s", prefix, String.valueOf(o)); - } - - } - -} +package io.dico.dicore; + +import java.util.logging.Logger; + +public interface Logging { + + void info(Object o); + + void warn(Object o); + + void error(Object o); + + void debug(Object o); + + void setDebugging(boolean debugging); + + boolean isDebugging(); + + class RootLogging implements Logging { + private final String prefix; + private final Logger root; + private boolean debugging; + + public RootLogging(String prefix, Logger root, boolean debugging) { + this.root = root; + this.prefix = prefix; + this.debugging = debugging; + } + + @Override + public void info(Object o) { + root.info(prefix(o)); + } + + @Override + public void warn(Object o) { + root.warning(prefix(o)); + } + + @Override + public void error(Object o) { + root.severe(prefix(o)); + } + + @Override + public void debug(Object o) { + if (debugging) { + root.info(String.format("[DEBUG] %s", prefix(o))); + } + } + + @Override + public boolean isDebugging() { + return debugging; + } + + @Override + public void setDebugging(boolean debugging) { + this.debugging = debugging; + } + + private String prefix(Object o) { + return String.format("[%s] %s", prefix, String.valueOf(o)); + } + } + + class SubLogging implements Logging { + protected String prefix; + private final Logging superLogger; + private boolean debugging; + + public SubLogging(String prefix, Logging superLogger, boolean debugging) { + this.superLogger = superLogger; + this.prefix = prefix; + this.debugging = debugging; + } + + @Override + public void info(Object o) { + superLogger.info(prefix(o)); + } + + @Override + public void warn(Object o) { + superLogger.warn(prefix(o)); + } + + @Override + public void error(Object o) { + superLogger.error(prefix(o)); + } + + @Override + public void debug(Object o) { + if (debugging) { + superLogger.info(String.format("[DEBUG] %s", prefix(o))); + } + } + + @Override + public boolean isDebugging() { + return debugging; + } + + @Override + public void setDebugging(boolean debugging) { + this.debugging = debugging; + } + + private String prefix(Object o) { + return String.format("[%s] %s", prefix, String.valueOf(o)); + } + + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java index b7b1ed4..2452d75 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java @@ -1,780 +1,780 @@ -package io.dico.dicore; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Reflective utilities - */ -@SuppressWarnings("unchecked") -public class Reflection { - private static final ExceptionHandler exceptionHandler; - private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers"); - private static Consumer errorTarget; - - private Reflection() { - - } - - static { - exceptionHandler = new ExceptionHandler() { - @Override - public void handle(Throwable ex) { - handleGenericException(ex); - } - - @Override - public Object handleGenericException(Throwable ex, Object... args) { - String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0]; - ExceptionHandler.log(errorTarget, action, ex); - return null; - } - }; - - // don't use method reference here: the current reference in System.out would be cached. - setErrorTarget(msg -> System.out.println(msg)); - } - - /** - * Sets the output where ReflectiveOperationException's and similar are sent. - * This defaults to {@link System#out}. - * - * @param target The new output - * @throws NullPointerException if target is null - */ - public static void setErrorTarget(Consumer target) { - errorTarget = Objects.requireNonNull(target); - } - - /** - * This search modifier tells the implementation that it should subsequently search superclasses for the field/method. - * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)} - * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])} - */ - public static final int DEEP_SEARCH = 0x1; - - /** - * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed. - */ - public static final int REMOVE_FINAL = 0x2; - - /** - * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name - * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set. - */ - public static final int IGNORE_PARAMS = 0x2; - - /* - ### FIELD METHODS ### - */ - - /** - * Search a field of any accessibility within the class or any of its superclasses. - * The first field with the given name that is found will be returned. - *

- * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param fieldName The name of the field - * //@param fieldType the type of the field, or null if it can be any. - * @return The field - * @throws NullPointerException if clazz is null or fieldName is null - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - */ - public static Field deepSearchField(Class clazz, String fieldName/*, Class fieldType*/) { - Class currentClass = clazz; - Field result; - do { - // throws NPE if class or fieldName is null - result = internalSearchField(clazz, fieldName); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName); - } - - /** - * Search a field of any accessibility within the class, but not its superclasses. - *

- * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the field - * @param fieldName The name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field does not exist - */ - public static Field restrictedSearchField(Class clazz, String fieldName) { - Field result = internalSearchField(clazz, fieldName); - if (result == null) { - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName); - } - return result; - } - - /** - * Searches for a field using the given search method. - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search in/from - * @param fieldName Name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field is not found - */ - public static Field searchField(int modifiers, Class clazz, String fieldName) { - Field result; - if ((modifiers & DEEP_SEARCH) != 0) { - result = deepSearchField(clazz, fieldName); - } else { - result = restrictedSearchField(clazz, fieldName); - } - if ((modifiers & REMOVE_FINAL) != 0) { - removeFinalModifier(result); - } - return result; - } - - /** - * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchField(Class, String) - */ - private static Field internalSearchField(Class clazz, String fieldName) { - Field result; - try { - // throws NullPointerException if either clazz or fieldName are null. - result = clazz.getDeclaredField(fieldName); - } catch (NoSuchFieldException | SecurityException ex) { - return null; - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Attempts to remove existing final modifier of the given field - * This method should always return true. - * - * @param field The field whose final modifier to remove - * @return true if the field most definitely has no final modifier after this call - * @throws NullPointerException if field is null - */ - public static boolean removeFinalModifier(Field field) { - Objects.requireNonNull(field); - try { - int modifiers = (int) fieldModifiersField.get(field); - if (modifiers != (modifiers &= ~Modifier.FINAL)) { - fieldModifiersField.set(field, modifiers); - } - return true; - } catch (Exception ex) { - return false; - } - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getFieldValue(Class, String, Object) - */ - public static T getFieldValue(Object instance, String fieldName) { - return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getFieldValue(Field, Object) - */ - public static T getFieldValue(Class clazz, String fieldName, Object instance) { - return getFieldValue(restrictedSearchField(clazz, fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #getFieldValue(Field, Object) - */ - public static T getFieldValue(int modifiers, Class clazz, String fieldName, Object instance) { - return getFieldValue(searchField(modifiers, clazz, fieldName), instance); - } - - /** - * Gets field value of the given field and the given instance - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field the field - * @param instance The instance whose field value to get - * @param The expected/known field type - * @return The field value - */ - public static T getFieldValue(Field field, Object instance) { - return exceptionHandler.supplySafe(() -> (T) field.get(instance)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static T getStaticFieldValue(Class clazz, String fieldName) { - return getStaticFieldValue(restrictedSearchField(clazz, fieldName)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static T getStaticFieldValue(int modifiers, Class clazz, String fieldName) { - return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); - } - - /** - * Gets static field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - *

- * Equivalent to the call {@code getFieldValue(field, (Object) null)} - * - * @param field the field - * @param The expected/known field type - * @return The field value - * @see #getFieldValue(Field, Object) - */ - public static T getStaticFieldValue(Field field) { - return getFieldValue(field, (Object) null); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue the new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #setFieldValue(Class, String, Object, Object) - */ - public static void setFieldValue(Object instance, String fieldName, Object newValue) { - setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param instance The field owner - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(Class clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(int modifiers, Class clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue); - } - - /** - * Sets a field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param instance The field owner - * @param newValue The new field value - */ - public static void setFieldValue(Field field, Object instance, Object newValue) { - exceptionHandler.runSafe(() -> field.set(instance, newValue)); - } - - /** - * Sets static field value of the field name fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(Class clazz, String fieldName, Object newValue) { - setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue); - } - - /** - * Sets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(int modifiers, Class clazz, String fieldName, Object newValue) { - setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue); - } - - /** - * Sets a static field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param newValue The new field value - */ - public static void setStaticFieldValue(Field field, Object newValue) { - setFieldValue(field, (Object) null, newValue); - } - - /* - ### METHOD METHODS ### - */ - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(Class clazz, String methodName, Class... parameterTypes) { - return deepSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Class currentClass = clazz; - Method result; - do { - // throws NPE if class or methodName is null - result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(Class clazz, String methodName, Class... parameterTypes) { - return restrictedSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes); - if (result == null) { - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName); - } - return result; - } - - /** - * Searches for a method using the given search method. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - */ - public static Method searchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - if ((modifiers & DEEP_SEARCH) != 0) { - return deepSearchMethod(modifiers, clazz, methodName, parameterTypes); - } else { - return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes); - } - } - - /** - * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - private static Method internalSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Method result = null; - - if ((modifiers & IGNORE_PARAMS) != 0) { - - // throws NullPointerException if either clazz or methodName are null. - methodName = methodName.intern(); - for (Method method : clazz.getDeclaredMethods()) { - // all method names are interned. Identity comparison is much faster. - if (method.getName() == methodName) { - result = method; - break; - } - } - - if (result == null) { - return null; - } - - } else { - - try { - // throws NullPointerException if either clazz or methodName are null. - result = clazz.getDeclaredMethod(methodName, parameterTypes); - } catch (NoSuchMethodException | SecurityException ex) { - return null; - } - - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(Class clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(int modifiers, Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(int modifiers, Class clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args); - } - - /** - * Invokes the method with the given instance and arguments - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - */ - public static T invokeMethod(Method method, Object instance, Object... args) { - return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args)); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static T invokeStaticMethod(Class clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static T invokeStaticMethod(int modifiers, Class clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); - } - - /** - * Invokes the static method with the given arguments - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeStaticMethod(Method method, Object... args) { - return invokeMethod(method, (Object) null, args); - } - -} +package io.dico.dicore; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Reflective utilities + */ +@SuppressWarnings("unchecked") +public class Reflection { + private static final ExceptionHandler exceptionHandler; + private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers"); + private static Consumer errorTarget; + + private Reflection() { + + } + + static { + exceptionHandler = new ExceptionHandler() { + @Override + public void handle(Throwable ex) { + handleGenericException(ex); + } + + @Override + public Object handleGenericException(Throwable ex, Object... args) { + String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0]; + ExceptionHandler.log(errorTarget, action, ex); + return null; + } + }; + + // don't use method reference here: the current reference in System.out would be cached. + setErrorTarget(msg -> System.out.println(msg)); + } + + /** + * Sets the output where ReflectiveOperationException's and similar are sent. + * This defaults to {@link System#out}. + * + * @param target The new output + * @throws NullPointerException if target is null + */ + public static void setErrorTarget(Consumer target) { + errorTarget = Objects.requireNonNull(target); + } + + /** + * This search modifier tells the implementation that it should subsequently search superclasses for the field/method. + * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)} + * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])} + */ + public static final int DEEP_SEARCH = 0x1; + + /** + * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed. + */ + public static final int REMOVE_FINAL = 0x2; + + /** + * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name + * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set. + */ + public static final int IGNORE_PARAMS = 0x2; + + /* + ### FIELD METHODS ### + */ + + /** + * Search a field of any accessibility within the class or any of its superclasses. + * The first field with the given name that is found will be returned. + *

+ * If a field is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. + * + * @param clazz The lowest class in the ladder to start searching from + * @param fieldName The name of the field + * //@param fieldType the type of the field, or null if it can be any. + * @return The field + * @throws NullPointerException if clazz is null or fieldName is null + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + */ + public static Field deepSearchField(Class clazz, String fieldName/*, Class fieldType*/) { + Class currentClass = clazz; + Field result; + do { + // throws NPE if class or fieldName is null + result = internalSearchField(clazz, fieldName); + if (result != null) { + return result; + } + currentClass = currentClass.getSuperclass(); + } while (currentClass != null); + + throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName); + } + + /** + * Search a field of any accessibility within the class, but not its superclasses. + *

+ * If a field is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. + * + * @param clazz The only class to search for the field + * @param fieldName The name of the field + * @return The field + * @throws NullPointerException if clazz or fieldName is null + * @throws IllegalArgumentException if the field does not exist + */ + public static Field restrictedSearchField(Class clazz, String fieldName) { + Field result = internalSearchField(clazz, fieldName); + if (result == null) { + throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName); + } + return result; + } + + /** + * Searches for a field using the given search method. + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search in/from + * @param fieldName Name of the field + * @return The field + * @throws NullPointerException if clazz or fieldName is null + * @throws IllegalArgumentException if the field is not found + */ + public static Field searchField(int modifiers, Class clazz, String fieldName) { + Field result; + if ((modifiers & DEEP_SEARCH) != 0) { + result = deepSearchField(clazz, fieldName); + } else { + result = restrictedSearchField(clazz, fieldName); + } + if ((modifiers & REMOVE_FINAL) != 0) { + removeFinalModifier(result); + } + return result; + } + + /** + * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException + * @see #restrictedSearchField(Class, String) + */ + private static Field internalSearchField(Class clazz, String fieldName) { + Field result; + try { + // throws NullPointerException if either clazz or fieldName are null. + result = clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException | SecurityException ex) { + return null; + } + + if (!result.isAccessible()) try { + result.setAccessible(true); + } catch (SecurityException ignored) { + + } + + return result; + } + + /** + * Attempts to remove existing final modifier of the given field + * This method should always return true. + * + * @param field The field whose final modifier to remove + * @return true if the field most definitely has no final modifier after this call + * @throws NullPointerException if field is null + */ + public static boolean removeFinalModifier(Field field) { + Objects.requireNonNull(field); + try { + int modifiers = (int) fieldModifiersField.get(field); + if (modifiers != (modifiers &= ~Modifier.FINAL)) { + fieldModifiersField.set(field, modifiers); + } + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #getFieldValue(Class, String, Object) + */ + public static T getFieldValue(Object instance, String fieldName) { + return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance); + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #getFieldValue(Field, Object) + */ + public static T getFieldValue(Class clazz, String fieldName, Object instance) { + return getFieldValue(restrictedSearchField(clazz, fieldName), instance); + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #getFieldValue(Field, Object) + */ + public static T getFieldValue(int modifiers, Class clazz, String fieldName, Object instance) { + return getFieldValue(searchField(modifiers, clazz, fieldName), instance); + } + + /** + * Gets field value of the given field and the given instance + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field the field + * @param instance The instance whose field value to get + * @param The expected/known field type + * @return The field value + */ + public static T getFieldValue(Field field, Object instance) { + return exceptionHandler.supplySafe(() -> (T) field.get(instance)); + } + + /** + * Gets static field value of the field named fieldName + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #getStaticFieldValue(Field) + */ + public static T getStaticFieldValue(Class clazz, String fieldName) { + return getStaticFieldValue(restrictedSearchField(clazz, fieldName)); + } + + /** + * Gets static field value of the field named fieldName + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #getStaticFieldValue(Field) + */ + public static T getStaticFieldValue(int modifiers, Class clazz, String fieldName) { + return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); + } + + /** + * Gets static field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + *

+ * Equivalent to the call {@code getFieldValue(field, (Object) null)} + * + * @param field the field + * @param The expected/known field type + * @return The field value + * @see #getFieldValue(Field, Object) + */ + public static T getStaticFieldValue(Field field) { + return getFieldValue(field, (Object) null); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param instance The instance whose field value to set + * @param fieldName the name of the field + * @param newValue the new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #setFieldValue(Class, String, Object, Object) + */ + public static void setFieldValue(Object instance, String fieldName, Object newValue) { + setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param instance The field owner + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #setFieldValue(Field, Object, Object) + */ + public static void setFieldValue(Class clazz, String fieldName, Object instance, Object newValue) { + setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param instance The instance whose field value to set + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #setFieldValue(Field, Object, Object) + */ + public static void setFieldValue(int modifiers, Class clazz, String fieldName, Object instance, Object newValue) { + setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue); + } + + /** + * Sets a field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field The field + * @param instance The field owner + * @param newValue The new field value + */ + public static void setFieldValue(Field field, Object instance, Object newValue) { + exceptionHandler.runSafe(() -> field.set(instance, newValue)); + } + + /** + * Sets static field value of the field name fieldName + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #setStaticFieldValue(Field, Object) + */ + public static void setStaticFieldValue(Class clazz, String fieldName, Object newValue) { + setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue); + } + + /** + * Sets static field value of the field named fieldName + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #setStaticFieldValue(Field, Object) + */ + public static void setStaticFieldValue(int modifiers, Class clazz, String fieldName, Object newValue) { + setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue); + } + + /** + * Sets a static field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field The field + * @param newValue The new field value + */ + public static void setStaticFieldValue(Field field, Object newValue) { + setFieldValue(field, (Object) null, newValue); + } + + /* + ### METHOD METHODS ### + */ + + /** + * Search a method of any accessibility within the class or any of its superclasses. + * The first method with the given name that is found will be returned. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param clazz The lowest class in the ladder to start searching from + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz is null or methodName is null + * @throws IllegalArgumentException if the method doesn't exist + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + public static Method deepSearchMethod(Class clazz, String methodName, Class... parameterTypes) { + return deepSearchMethod(0, clazz, methodName, parameterTypes); + } + + /** + * Search a method of any accessibility within the class or any of its superclasses. + * The first method with the given name that is found will be returned. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} + * @param clazz The lowest class in the ladder to start searching from + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz is null or methodName is null + * @throws IllegalArgumentException if the method doesn't exist + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + public static Method deepSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Class currentClass = clazz; + Method result; + do { + // throws NPE if class or methodName is null + result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes); + if (result != null) { + return result; + } + currentClass = currentClass.getSuperclass(); + } while (currentClass != null); + + throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName); + } + + /** + * Search a method of any accessibility within the class, but not its superclasses. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param clazz The only class to search for the method + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method does not exist + */ + public static Method restrictedSearchMethod(Class clazz, String methodName, Class... parameterTypes) { + return restrictedSearchMethod(0, clazz, methodName, parameterTypes); + } + + /** + * Search a method of any accessibility within the class, but not its superclasses. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} + * @param clazz The only class to search for the method + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method does not exist + */ + public static Method restrictedSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes); + if (result == null) { + throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName); + } + return result; + } + + /** + * Searches for a method using the given search method. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + */ + public static Method searchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + if ((modifiers & DEEP_SEARCH) != 0) { + return deepSearchMethod(modifiers, clazz, methodName, parameterTypes); + } else { + return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes); + } + } + + /** + * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + private static Method internalSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Method result = null; + + if ((modifiers & IGNORE_PARAMS) != 0) { + + // throws NullPointerException if either clazz or methodName are null. + methodName = methodName.intern(); + for (Method method : clazz.getDeclaredMethods()) { + // all method names are interned. Identity comparison is much faster. + if (method.getName() == methodName) { + result = method; + break; + } + } + + if (result == null) { + return null; + } + + } else { + + try { + // throws NullPointerException if either clazz or methodName are null. + result = clazz.getDeclaredMethod(methodName, parameterTypes); + } catch (NoSuchMethodException | SecurityException ex) { + return null; + } + + } + + if (!result.isAccessible()) try { + result.setAccessible(true); + } catch (SecurityException ignored) { + + } + + return result; + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if instance or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(Object instance, String methodName, Object... args) { + return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * as well as the modifier {@link #IGNORE_PARAMS} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(Class clazz, String methodName, Object instance, Object... args) { + return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if instance or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(int modifiers, Object instance, String methodName, Object... args) { + return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(int modifiers, Class clazz, String methodName, Object instance, Object... args) { + return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args); + } + + /** + * Invokes the method with the given instance and arguments + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param method The method to invoke + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + */ + public static T invokeMethod(Method method, Object instance, Object... args) { + return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args)); + } + + /** + * Invokes the static method named methodName with the given arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * as well as the modifier {@link #IGNORE_PARAMS} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeStaticMethod(Method, Object...) + */ + public static T invokeStaticMethod(Class clazz, String methodName, Object... args) { + return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args); + } + + /** + * Invokes the static method named methodName with the given arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeStaticMethod(Method, Object...) + */ + public static T invokeStaticMethod(int modifiers, Class clazz, String methodName, Object... args) { + return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); + } + + /** + * Invokes the static method with the given arguments + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param method The method to invoke + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeStaticMethod(Method method, Object... args) { + return invokeMethod(method, (Object) null, args); + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java index 6596b65..d4486ed 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java +++ b/dicore3/core/src/main/java/io/dico/dicore/SetBasedWhitelist.java @@ -1,59 +1,59 @@ -package io.dico.dicore; - -import org.bukkit.configuration.ConfigurationSection; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class SetBasedWhitelist implements Whitelist { - private final Set set; - private final boolean blacklist; - - public SetBasedWhitelist(Object[] array, boolean blacklist) { - this(Arrays.asList(array), blacklist); - } - - public SetBasedWhitelist(ConfigurationSection section, Function parser) { - this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()), - section.getBoolean("blacklist", false)); - } - - @SuppressWarnings("unchecked") - public SetBasedWhitelist(Collection collection, boolean blacklist) { - Set set; - if (collection.isEmpty()) { - set = Collections.emptySet(); - } else if (collection.iterator().next() instanceof Enum) { - set = EnumSet.copyOf(collection); - } else if (collection instanceof Set) { - set = (Set) collection; - } else { - set = new HashSet<>(collection); - } - - this.set = set; - this.blacklist = blacklist; - } - - @Override - public boolean isWhitelisted(Object o) { - return blacklist != set.contains(o); - } - - @SuppressWarnings("unchecked") - @Override - public String toString() { - return (blacklist ? "Blacklist" : "Whitelist") + "{" - + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}"; - } - - public Set getSet() { - return set; - } - - public boolean isBlacklist() { - return blacklist; - } - -} +package io.dico.dicore; + +import org.bukkit.configuration.ConfigurationSection; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class SetBasedWhitelist implements Whitelist { + private final Set set; + private final boolean blacklist; + + public SetBasedWhitelist(Object[] array, boolean blacklist) { + this(Arrays.asList(array), blacklist); + } + + public SetBasedWhitelist(ConfigurationSection section, Function parser) { + this(section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toList()), + section.getBoolean("blacklist", false)); + } + + @SuppressWarnings("unchecked") + public SetBasedWhitelist(Collection collection, boolean blacklist) { + Set set; + if (collection.isEmpty()) { + set = Collections.emptySet(); + } else if (collection.iterator().next() instanceof Enum) { + set = EnumSet.copyOf(collection); + } else if (collection instanceof Set) { + set = (Set) collection; + } else { + set = new HashSet<>(collection); + } + + this.set = set; + this.blacklist = blacklist; + } + + @Override + public boolean isWhitelisted(Object o) { + return blacklist != set.contains(o); + } + + @SuppressWarnings("unchecked") + @Override + public String toString() { + return (blacklist ? "Blacklist" : "Whitelist") + "{" + + String.join(", ", (CharSequence[]) set.stream().map(String::valueOf).toArray(String[]::new)) + "}"; + } + + public Set getSet() { + return set; + } + + public boolean isBlacklist() { + return blacklist; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java index 44659fa..afcae02 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/SpigotUtil.java @@ -1,408 +1,408 @@ -package io.dico.dicore; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.entity.*; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.material.Attachable; -import org.bukkit.material.MaterialData; -import org.bukkit.projectiles.ProjectileSource; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class SpigotUtil { - - private SpigotUtil() { - throw new UnsupportedOperationException(); - } - - public static World matchWorld(String input) { - try { - UUID uid = UUID.fromString(input); - World world = Bukkit.getWorld(uid); - if (world != null) { - return world; - } - } catch (IllegalArgumentException ignored) { - } - - World result = Bukkit.getWorld(input); - if (result == null) { - input = input.toLowerCase().replace("_", "").replaceAll("[-_]", ""); - for (World world : Bukkit.getWorlds()) { - if (world.getName().toLowerCase().equals(input)) { - result = world; - break; - } - } - } - - return result; - } - - public static Block getSupportingBlock(Block block) { - MaterialData data = block.getState().getData(); - if (data instanceof Attachable) { - BlockFace attachedOn = ((Attachable) data).getAttachedFace(); - return block.getRelative(attachedOn); - } - return null; - } - - public static boolean isItemPresent(ItemStack stack) { - return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0; - } - - public static boolean removeItems(Inventory from, ItemStack item, int amount) { - for (Map.Entry entry : from.all(item.getType()).entrySet()) { - ItemStack stack = entry.getValue(); - if (item.isSimilar(stack)) { - amount -= stack.getAmount(); - int stackAmount = -Math.min(0, amount); - if (stackAmount == 0) { - from.setItem(entry.getKey(), null); - } else { - stack.setAmount(stackAmount); - } - } - } - return amount <= 0; - } - - public static BlockFace yawToFace(float yaw) { - if ((yaw %= 360) < 0) - yaw += 360; - if (45 <= yaw && yaw < 135) - return BlockFace.WEST; - if (135 <= yaw && yaw < 225) - return BlockFace.NORTH; - if (225 <= yaw && yaw < 315) - return BlockFace.EAST; - return BlockFace.SOUTH; - } - - public static void addItems(InventoryHolder entity, ItemStack... items) { - Location dropLocation; - if (entity instanceof Entity) { - dropLocation = ((Entity) entity).getLocation(); - } else if (entity instanceof BlockState) { - dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5); - } else { - throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity); - } - World world = dropLocation.getWorld(); - for (ItemStack toDrop : entity.getInventory().addItem(items).values()) { - world.dropItemNaturally(dropLocation, toDrop); - } - } - - public static String asJsonString(Object object) { - return asJsonString(null, object, 0); - } - - public static String asJsonString(String key, Object object, int indentation) { - String indent = new String(new char[indentation * 2]).replace('\0', ' '); - StringBuilder builder = new StringBuilder(indent); - if (key != null) { - builder.append(key).append(": "); - } - if (object instanceof ConfigurationSerializable) { - object = ((ConfigurationSerializable) object).serialize(); - } - if (object instanceof Map) { - builder.append("{\n"); - Map map = (Map) object; - for (Map.Entry entry : map.entrySet()) { - builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1)); - } - builder.append(indent).append("}"); - } else if (object instanceof List) { - builder.append("[\n"); - List list = (List) object; - for (Object entry : list) { - builder.append(asJsonString(null, entry, indentation + 1)); - } - builder.append(indent).append("]"); - } else { - builder.append(String.valueOf(object)); - } - return builder.append(",\n").toString(); - } - - public static String asJsonString(String key, Object object, int indentation, BiConsumer, StringBuilder> listHeader) { - String indent = new String(new char[indentation * 2]).replace('\0', ' '); - StringBuilder builder = new StringBuilder(indent); - if (key != null) { - builder.append(key).append(": "); - } - if (object instanceof ConfigurationSerializable) { - object = ((ConfigurationSerializable) object).serialize(); - } - if (object instanceof Map) { - builder.append("{\n"); - Map map = (Map) object; - for (Map.Entry entry : map.entrySet()) { - builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader)); - } - builder.append(indent).append("}"); - } else if (object instanceof List) { - builder.append("["); - List list = (List) object; - listHeader.accept(list, builder); - builder.append("\n"); - for (Object entry : list) { - builder.append(asJsonString(null, entry, indentation + 1, listHeader)); - } - builder.append(indent).append("]"); - } else { - builder.append(String.valueOf(object)); - } - return builder.append(",\n").toString(); - } - - public static BlockFace estimateDirectionTo(Location from, Location to) { - double dx = from.getX() - to.getX(); - double dz = from.getZ() - to.getZ(); - - boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0; - double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz); - dx *= f; - dz *= f; - - double other = Math.abs(xGreater ? dz : dx); - - if (other <= .5) { - return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH); - } - - if (other < 1.5) { - 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.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); - } - - public static Entity findEntityFromDamager(Entity damager, EntityType searched) { - if (damager.getType() == searched) { - return damager; - } - - if (damager instanceof Projectile) { - ProjectileSource shooter = ((Projectile) damager).getShooter(); - if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) { - return (Entity) shooter; - } - return null; - } - - if (damager.getType() == EntityType.PRIMED_TNT) { - Entity source = ((TNTPrimed) damager).getSource(); - if (source.getType() == searched) { - return source; - } - } - - return null; - } - - public static int xpForNextLevel(int currentLevel) { - if (currentLevel >= 30) { - return 112 + (currentLevel - 30) * 9; - } - - if (currentLevel >= 15) { - return 37 + (currentLevel - 15) * 5; - } - - return 7 + currentLevel * 2; - } - - public static int removeExp(Player entity, int xp) { - int total = entity.getTotalExperience(); - if (xp > total) { - xp = total; - } - - int level = entity.getLevel(); - if (level < 0) { - return 0; - } - - int removed = 0; - int xpForNextLevel = xpForNextLevel(level); - int current = (int) entity.getExp() * xpForNextLevel; - - if (xp > current) { - xp -= current; - total -= current; - removed += current; - - if (level == 0) { - entity.setExp(0F); - entity.setTotalExperience(total); - return removed; - } - } else { - current -= xp; - total -= xp; - removed += xp; - - entity.setExp((float) current / xpForNextLevel); - entity.setTotalExperience(total); - return removed; - } - - do { - xpForNextLevel = xpForNextLevel(--level); - if (xpForNextLevel >= xp) { - total -= xp; - removed += xp; - - entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp)); - entity.setTotalExperience(total); - entity.setLevel(level); - return removed; - } - - xp -= xpForNextLevel; - total -= xpForNextLevel; - removed += xpForNextLevel; - } while (level > 0); - - entity.setExp(0F); - entity.setTotalExperience(0); - entity.setLevel(0); - return removed; - } - - public static int getTotalExp(Player entity) { - int rv = 0; - int level = Math.min(entity.getLevel(), 20000); - for (int i = 0; i < level; i++) { - rv += xpForNextLevel(i); - } - rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level); - return rv; - } - - public static double getTotalExpLevels(Player entity) { - int xp = entity.getTotalExperience(); - - int level = 0; - while (xp > 0 && level < 20000) { - int needed = xpForNextLevel(level); - if (needed > xp) { - return level + ((double) xp / needed); - } - xp -= needed; - level++; - } - - return level; - } - - public static int getNearbyPlayerCount(Player origin, double range, Predicate predicate) { - List entities = origin.getNearbyEntities(range, range, range); - int result = 0; - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { - result++; - } - } - return result; - } - - public static void getNearbyPlayers(Player origin, double range, Collection collection, Predicate predicate) { - List entities = origin.getNearbyEntities(range, range, range); - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { - collection.add((Player) entity); - } - } - } - - public static void forEachNearbyPlayer(Player origin, double range, Consumer action) { - List entities = origin.getNearbyEntities(range, range, range); - for (Entity entity : entities) { - if (entity.getType() == EntityType.PLAYER) { - action.accept((Player) entity); - } - } - } - - public static double distanceSquared(Location first, Location second) { - double dx = first.getX() - second.getX(); - double dy = first.getY() - second.getY(); - double dz = first.getZ() - second.getZ(); - - return dx * dx + dy * dy + dz * dz; - } - - - public static Iterator findNearbyEntities(Entity origin, boolean includeSelf, Predicate predicate, double horizontalRange, double verticalRange) { - Objects.requireNonNull(origin); - return new Iterator() { - Entity next; - List nearby; - int index = 0; - int size; - - { - if (includeSelf) { - next = origin; - } else { - next = findNext(); - } - } - - Entity findNext() { - if (nearby == null) { - nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange); - size = nearby.size(); - } - - while (index < size) { - Entity e = nearby.get(index++); - if (predicate.test(e)) { - return e; - } - } - - return null; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @Override - public T next() { - if (next == null) { - throw new NoSuchElementException(); - } - Entity result = next; - next = findNext(); - //noinspection unchecked - return (T) result; - } - - }; - } - - -} +package io.dico.dicore; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.entity.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.Attachable; +import org.bukkit.material.MaterialData; +import org.bukkit.projectiles.ProjectileSource; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class SpigotUtil { + + private SpigotUtil() { + throw new UnsupportedOperationException(); + } + + public static World matchWorld(String input) { + try { + UUID uid = UUID.fromString(input); + World world = Bukkit.getWorld(uid); + if (world != null) { + return world; + } + } catch (IllegalArgumentException ignored) { + } + + World result = Bukkit.getWorld(input); + if (result == null) { + input = input.toLowerCase().replace("_", "").replaceAll("[-_]", ""); + for (World world : Bukkit.getWorlds()) { + if (world.getName().toLowerCase().equals(input)) { + result = world; + break; + } + } + } + + return result; + } + + public static Block getSupportingBlock(Block block) { + MaterialData data = block.getState().getData(); + if (data instanceof Attachable) { + BlockFace attachedOn = ((Attachable) data).getAttachedFace(); + return block.getRelative(attachedOn); + } + return null; + } + + public static boolean isItemPresent(ItemStack stack) { + return stack != null && stack.getType() != Material.AIR && stack.getAmount() > 0; + } + + public static boolean removeItems(Inventory from, ItemStack item, int amount) { + for (Map.Entry entry : from.all(item.getType()).entrySet()) { + ItemStack stack = entry.getValue(); + if (item.isSimilar(stack)) { + amount -= stack.getAmount(); + int stackAmount = -Math.min(0, amount); + if (stackAmount == 0) { + from.setItem(entry.getKey(), null); + } else { + stack.setAmount(stackAmount); + } + } + } + return amount <= 0; + } + + public static BlockFace yawToFace(float yaw) { + if ((yaw %= 360) < 0) + yaw += 360; + if (45 <= yaw && yaw < 135) + return BlockFace.WEST; + if (135 <= yaw && yaw < 225) + return BlockFace.NORTH; + if (225 <= yaw && yaw < 315) + return BlockFace.EAST; + return BlockFace.SOUTH; + } + + public static void addItems(InventoryHolder entity, ItemStack... items) { + Location dropLocation; + if (entity instanceof Entity) { + dropLocation = ((Entity) entity).getLocation(); + } else if (entity instanceof BlockState) { + dropLocation = ((BlockState) entity).getLocation().add(0.5, 1, 0.5); + } else { + throw new IllegalArgumentException("Can't find location of this InventoryHolder: " + entity); + } + World world = dropLocation.getWorld(); + for (ItemStack toDrop : entity.getInventory().addItem(items).values()) { + world.dropItemNaturally(dropLocation, toDrop); + } + } + + public static String asJsonString(Object object) { + return asJsonString(null, object, 0); + } + + public static String asJsonString(String key, Object object, int indentation) { + String indent = new String(new char[indentation * 2]).replace('\0', ' '); + StringBuilder builder = new StringBuilder(indent); + if (key != null) { + builder.append(key).append(": "); + } + if (object instanceof ConfigurationSerializable) { + object = ((ConfigurationSerializable) object).serialize(); + } + if (object instanceof Map) { + builder.append("{\n"); + Map map = (Map) object; + for (Map.Entry entry : map.entrySet()) { + builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1)); + } + builder.append(indent).append("}"); + } else if (object instanceof List) { + builder.append("[\n"); + List list = (List) object; + for (Object entry : list) { + builder.append(asJsonString(null, entry, indentation + 1)); + } + builder.append(indent).append("]"); + } else { + builder.append(String.valueOf(object)); + } + return builder.append(",\n").toString(); + } + + public static String asJsonString(String key, Object object, int indentation, BiConsumer, StringBuilder> listHeader) { + String indent = new String(new char[indentation * 2]).replace('\0', ' '); + StringBuilder builder = new StringBuilder(indent); + if (key != null) { + builder.append(key).append(": "); + } + if (object instanceof ConfigurationSerializable) { + object = ((ConfigurationSerializable) object).serialize(); + } + if (object instanceof Map) { + builder.append("{\n"); + Map map = (Map) object; + for (Map.Entry entry : map.entrySet()) { + builder.append(asJsonString(String.valueOf(entry.getKey()), entry.getValue(), indentation + 1, listHeader)); + } + builder.append(indent).append("}"); + } else if (object instanceof List) { + builder.append("["); + List list = (List) object; + listHeader.accept(list, builder); + builder.append("\n"); + for (Object entry : list) { + builder.append(asJsonString(null, entry, indentation + 1, listHeader)); + } + builder.append(indent).append("]"); + } else { + builder.append(String.valueOf(object)); + } + return builder.append(",\n").toString(); + } + + public static BlockFace estimateDirectionTo(Location from, Location to) { + double dx = from.getX() - to.getX(); + double dz = from.getZ() - to.getZ(); + + boolean xGreater = Math.abs(dx) - Math.abs(dz) > 0; + double f = xGreater ? 2 / Math.abs(dx) : 2 / Math.abs(dz); + dx *= f; + dz *= f; + + double other = Math.abs(xGreater ? dz : dx); + + if (other <= .5) { + return xGreater ? (dx < 0 ? BlockFace.WEST : BlockFace.EAST) : (dz < 0 ? BlockFace.NORTH : BlockFace.SOUTH); + } + + if (other < 1.5) { + 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.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); + } + + public static Entity findEntityFromDamager(Entity damager, EntityType searched) { + if (damager.getType() == searched) { + return damager; + } + + if (damager instanceof Projectile) { + ProjectileSource shooter = ((Projectile) damager).getShooter(); + if (shooter instanceof Entity && ((Entity) shooter).getType() == searched) { + return (Entity) shooter; + } + return null; + } + + if (damager.getType() == EntityType.PRIMED_TNT) { + Entity source = ((TNTPrimed) damager).getSource(); + if (source.getType() == searched) { + return source; + } + } + + return null; + } + + public static int xpForNextLevel(int currentLevel) { + if (currentLevel >= 30) { + return 112 + (currentLevel - 30) * 9; + } + + if (currentLevel >= 15) { + return 37 + (currentLevel - 15) * 5; + } + + return 7 + currentLevel * 2; + } + + public static int removeExp(Player entity, int xp) { + int total = entity.getTotalExperience(); + if (xp > total) { + xp = total; + } + + int level = entity.getLevel(); + if (level < 0) { + return 0; + } + + int removed = 0; + int xpForNextLevel = xpForNextLevel(level); + int current = (int) entity.getExp() * xpForNextLevel; + + if (xp > current) { + xp -= current; + total -= current; + removed += current; + + if (level == 0) { + entity.setExp(0F); + entity.setTotalExperience(total); + return removed; + } + } else { + current -= xp; + total -= xp; + removed += xp; + + entity.setExp((float) current / xpForNextLevel); + entity.setTotalExperience(total); + return removed; + } + + do { + xpForNextLevel = xpForNextLevel(--level); + if (xpForNextLevel >= xp) { + total -= xp; + removed += xp; + + entity.setExp(1F / xpForNextLevel * (xpForNextLevel - xp)); + entity.setTotalExperience(total); + entity.setLevel(level); + return removed; + } + + xp -= xpForNextLevel; + total -= xpForNextLevel; + removed += xpForNextLevel; + } while (level > 0); + + entity.setExp(0F); + entity.setTotalExperience(0); + entity.setLevel(0); + return removed; + } + + public static int getTotalExp(Player entity) { + int rv = 0; + int level = Math.min(entity.getLevel(), 20000); + for (int i = 0; i < level; i++) { + rv += xpForNextLevel(i); + } + rv += Math.min(1F, Math.max(0F, entity.getExp())) * xpForNextLevel(level); + return rv; + } + + public static double getTotalExpLevels(Player entity) { + int xp = entity.getTotalExperience(); + + int level = 0; + while (xp > 0 && level < 20000) { + int needed = xpForNextLevel(level); + if (needed > xp) { + return level + ((double) xp / needed); + } + xp -= needed; + level++; + } + + return level; + } + + public static int getNearbyPlayerCount(Player origin, double range, Predicate predicate) { + List entities = origin.getNearbyEntities(range, range, range); + int result = 0; + for (Entity entity : entities) { + if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { + result++; + } + } + return result; + } + + public static void getNearbyPlayers(Player origin, double range, Collection collection, Predicate predicate) { + List entities = origin.getNearbyEntities(range, range, range); + for (Entity entity : entities) { + if (entity.getType() == EntityType.PLAYER && predicate.test((Player) entity)) { + collection.add((Player) entity); + } + } + } + + public static void forEachNearbyPlayer(Player origin, double range, Consumer action) { + List entities = origin.getNearbyEntities(range, range, range); + for (Entity entity : entities) { + if (entity.getType() == EntityType.PLAYER) { + action.accept((Player) entity); + } + } + } + + public static double distanceSquared(Location first, Location second) { + double dx = first.getX() - second.getX(); + double dy = first.getY() - second.getY(); + double dz = first.getZ() - second.getZ(); + + return dx * dx + dy * dy + dz * dz; + } + + + public static Iterator findNearbyEntities(Entity origin, boolean includeSelf, Predicate predicate, double horizontalRange, double verticalRange) { + Objects.requireNonNull(origin); + return new Iterator() { + Entity next; + List nearby; + int index = 0; + int size; + + { + if (includeSelf) { + next = origin; + } else { + next = findNext(); + } + } + + Entity findNext() { + if (nearby == null) { + nearby = origin.getNearbyEntities(horizontalRange, verticalRange, horizontalRange); + size = nearby.size(); + } + + while (index < size) { + Entity e = nearby.get(index++); + if (predicate.test(e)) { + return e; + } + } + + return null; + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + if (next == null) { + throw new NoSuchElementException(); + } + Entity result = next; + next = findNext(); + //noinspection unchecked + return (T) result; + } + + }; + } + + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java index 62a389e..fb76755 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java +++ b/dicore3/core/src/main/java/io/dico/dicore/StringUtil.java @@ -1,473 +1,473 @@ -package io.dico.dicore; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - -public class StringUtil { - - public static String capitalize(String input) { - if (input.length() > 0) { - char first = input.charAt(0); - if (first != (first = Character.toUpperCase(first))) { - char[] result = input.toCharArray(); - result[0] = first; - return String.valueOf(result); - } - } - return input; - } - - /** - * Capitalizes the first character of the string or the first character of each word - * - * @param input the string to capitalize - * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of - * the input is capitalized. - * @return the capitalized string - */ - public static String capitalize(String input, char spaceChar) { - if (spaceChar == '\0') { - return capitalize(input); - } - - char[] result = null; - boolean capitalize = true; - for (int n = input.length(), i = 0; i < n; i++) { - char c = input.charAt(i); - if (capitalize && c != (c = Character.toUpperCase(c))) { - if (result == null) result = input.toCharArray(); - result[i] = c; - } - capitalize = c == spaceChar; - } - return result != null ? String.valueOf(result) : input; - } - - public static String capitalize(String input, char spaceChar, char newSpaceChar) { - if (newSpaceChar == '\0') { - return capitalize(input, spaceChar); - } - - char[] result = null; - boolean capitalize = true; - for (int n = input.length(), i = 0; i < n; i++) { - char c = input.charAt(i); - if (capitalize && c != (c = Character.toUpperCase(c))) { - if (result == null) result = input.toCharArray(); - result[i] = c; - } - if (capitalize = c == spaceChar) { - if (result == null) result = input.toCharArray(); - result[i] = newSpaceChar; - } - } - return result != null ? String.valueOf(result) : input; - } - - /** - * Returns a lowercase version of the input with _ replaced with a space. - * Mainly used for making names of enum constants readable. - * - * @param input - * @return a humanified version of @code input - */ - public static String humanify(String input) { - return input == null ? null : input.toLowerCase().replace('_', ' '); - } - - /** - * Enumerate the given items, separating them by ", " and finally by " and " - * - * @param words the items to enumerate (it's not really enumerating....) - * @return the enumerated string - */ - public static String enumerate(String... words) { - StringBuilder result = new StringBuilder(); - int size = words.length; - int secondLastIndex = size - 2; - for (int i = 0; i < size; i++) { - String word = words[i]; - if (word.isEmpty()) - continue; - result.append(word); - if (i < secondLastIndex) - result.append(", "); - else if (i == secondLastIndex) - result.append(" and "); - } - return result.toString(); - } - - public static String enumerate(String list, String regex) { - return enumerate(list.split(regex)); - } - - /** - * Return a formatted string of the length in millis, containing days, hours and minutes. - * - * @param length The delay in milliseconds - * @return the formatted string - */ - public static String getTimeLength(long length) { - int minute = 60000; // in millis - int hour = 60 * minute; - int day = 24 * hour; - - int minutes = (int) ((length / minute) % 60); - int hours = (int) ((length / hour) % 24); - int days = (int) (length / day); //returns floor - - String result = ""; // It will be splitted at "|" - if (days != 0) - result += days + " days|"; - if (hours != 0) - result += hours + " hours|"; - if (minutes != 0) - result += minutes + " minutes|"; - return enumerate(result.split("\\|")); - } - - /** - * Return a formatted String to represent the given time length, in the given units - * - * @param sourceAmount Amount of delay - * @param sourceUnit Unit of delay - * @param ifEmpty the String to return if the - * @param displayedUnits units displayed - * @return the formatted string - * @throws IllegalArgumentException if there are no displayed units - */ - public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) { - if (displayedUnits.length == 0) { - throw new IllegalArgumentException("No displayed units"); - } - Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest) - List segments = new ArrayList<>(displayedUnits.length); - for (TimeUnit unit : displayedUnits) { - long displayedAmount = unit.convert(sourceAmount, sourceUnit); - sourceAmount -= sourceUnit.convert(displayedAmount, unit); - if (displayedAmount > 0) { - String unitWord = unit.name().toLowerCase(); // plural - if (displayedAmount == 1) { - unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end - } - segments.add(displayedAmount + " " + unitWord); - } - } - return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()])); - } - - /** - * Returns the delay represented by a ban-like delay representation, in milliseconds - * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds. - *

- * Supported characters are s, m, h, d, w. - * Negative numbers are supported. - * - * @param input The input string - * @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). - */ - public static long getTimeLength(String input) { //if -1: error - long count = 0; - int i = 0; - while (i < input.length()) { - int num = 0; - char unit = '\0'; - boolean negate; - if (negate = input.charAt(i) == '-') { - i++; - } - do { - char c = input.charAt(i); - int digit = c - '0'; - if (0 <= digit && digit < 10) { - num = 10 * num + digit; - } else { - unit = c; - break; - } - } while (i < input.length()); - - long unitTime = getUnitTime(unit); - if (unitTime == -1) - throw new IllegalArgumentException(); - if (negate) { - unitTime = -unitTime; - } - count += (num * unitTime); - } - return count; - } - - /** - * Returns the time represented by the given unit character in milliseconds. - *

- * 's' -> 1000 - * 'm' -> 1000 * 60 - * 'h' -> 1000 * 60 * 60 - * 'd' -> 1000 * 60 * 60 * 24 - * 'w' -> 1000 * 60 * 60 * 24 * 7 - * anything else -> -1 - * - * @param unit The unit character, as shown above - * @return the millisecond delay represented by the unit - */ - public static long getUnitTime(char unit) { //if -1: no value found - switch (Character.toLowerCase(unit)) { - case 's': - return 1000; - case 'm': - return 1000 * 60; - case 'h': - return 1000 * 60 * 60; - case 'd': - return 1000 * 60 * 60 * 24; - case 'w': - return 1000 * 60 * 60 * 24 * 7; - default: - return -1; - } - } - - /** - * Computes a binary representation of the value. - * The returned representation always displays 64 bits. - * Every 8 bits, the digits are seperated by an _ - * The representation is prefixed by 0b. - *

- * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010 - * - * @param entry the value to represent in binary - * @return A binary representation of the long value - */ - public static String toBinaryString(long entry) { - String binary = Long.toBinaryString(entry); - 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)); - return "0b" + withUnderscores; - } - - /** - * 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) - * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE) - */ - public static String toEnumFormat(String name) { - StringBuilder result = new StringBuilder(name.length() + 2); - - boolean capital = true; - for (int i = 0, n = name.length(); i < n; i++) { - char c = name.charAt(i); - if (capital) { - capital = Character.isUpperCase(c); - } else if (Character.isUpperCase(c)) { - capital = true; - result.append('_'); - } - result.append(capital ? c : Character.toUpperCase(c)); - } - - return result.toString(); - } - - /** - * 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. - * - * @param target The String to query - * @param toReplace The sequence to replace - * @param with the replacing sequence - * @return the result - */ - public static String replaceKeepColours(String target, String toReplace, String with) { - int index = -toReplace.length(); - while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) { - String start = target.substring(0, index); - Formatting coloursBefore = Formatting.getFormats(start); - String after; - try { - after = target.substring(index + toReplace.length()); - } catch (IndexOutOfBoundsException e) { - after = ""; - } - target = start + with + coloursBefore + after; - } - return target; - } - - public static String replParam(String target, String param, Object repl) { - return replParam(target, param, repl, false); - } - - public static String replParams(String target, String[] params, Object[] repls) { - return replParams(target, params, repls, false, false); - } - - public static boolean replParams(String[] target, String[] params, Object[] repls) { - return replParams(target, 0, target.length, params, repls); - } - - public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, false); - } - - public static boolean replParams(List target, String[] params, Object[] repls) { - return replParams(target, 0, target.size(), params, repls); - } - - public static boolean replParams(List target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, false); - } - - public static String replParamAndTranslate(String target, String param, Object repl) { - return replParam(target, param, repl, true); - } - - public static String replParamsAndTranslate(String target, String[] params, Object[] repls) { - return replParams(target, params, repls, false, true); - } - - public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) { - return replParamsAndTranslate(target, 0, target.length, params, repls); - } - - public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, true); - } - - public static boolean replParamsAndTranslate(List target, String[] params, Object[] repls) { - return replParamsAndTranslate(target, 0, target.size(), params, repls); - } - - public static boolean replParamsAndTranslate(List target, int from, int to, String[] params, Object[] repls) { - return replParams(target, from, to, params, repls, true); - } - - private static String replParam(String target, String param, Object replacementObj, boolean translate) { - int idx = target.indexOf(param, 0); - if (idx == -1) { - return translate ? Formatting.translate(target) : target; - } - - String rep = replacementObj.toString(); - StringBuilder builder = new StringBuilder(target); - do { - builder.replace(idx, idx + param.length(), rep); - idx = builder.indexOf(param, idx + rep.length()); - } while (idx != -1); - - if (translate) { - Formatting.translate(builder); - } - - return builder.toString(); - } - - @SuppressWarnings("StringEquality") - private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) { - if (from < 0 || to < from || to > target.length) { - throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to); - } - - boolean change = false; - for (int i = from; i < to; i++) { - String val = target[i]; - if (val != (val = replParams(val, params, repls, true, translate))) { - target[i] = val; - change = true; - } - } - return change; - } - - @SuppressWarnings("StringEquality") - private static boolean replParams(List target, int from, int to, String[] params, Object[] repls, boolean translate) { - if (from < 0 || to < from || to > target.size()) { - throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to); - } - - boolean change = false; - if (target instanceof RandomAccess) { - for (int i = from; i < to; i++) { - String val = target.get(i); - if (val != (val = replParams(val, params, repls, true, translate))) { - target.set(i, val); - change = true; - } - } - } else { - ListIterator itr = target.listIterator(from); - for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) { - String val = itr.next(); - if (val != (val = replParams(val, params, repls, true, translate))) { - itr.set(val); - change = true; - } - } - } - return change; - } - - private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) { - int n = params.length; - if (n != repls.length) { - throw new IllegalArgumentException(); - } - - String param = null; - int idx = -1; - int i; - for (i = 0; i < n; i++) { - param = params[i]; - if (param == null) { - continue; - } - idx = target.indexOf(param, 0); - if (idx != -1) { - break; - } - } - - if (idx == -1) { - return translate ? Formatting.translate(target) : target; - } - - String repl = repls[i].toString(); - if (updateRepls) { - repls[i] = repl; - } - - StringBuilder builder = new StringBuilder(target); - do { - builder.replace(idx, idx + param.length(), repl); - idx = builder.indexOf(param, idx + repl.length()); - } while (idx != -1); - - for (i++; i < n; i++) { - param = params[i]; - if (param == null || (idx = builder.indexOf(param, 0)) == -1) { - continue; - } - - repl = repls[i].toString(); - if (updateRepls) { - repls[i] = repl; - } - - do { - builder.replace(idx, idx + param.length(), repl); - idx = builder.indexOf(param, idx + repl.length()); - } while (idx != -1); - } - - if (translate) { - Formatting.translate(builder); - } - - return builder.toString(); - } - -} +package io.dico.dicore; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +public class StringUtil { + + public static String capitalize(String input) { + if (input.length() > 0) { + char first = input.charAt(0); + if (first != (first = Character.toUpperCase(first))) { + char[] result = input.toCharArray(); + result[0] = first; + return String.valueOf(result); + } + } + return input; + } + + /** + * Capitalizes the first character of the string or the first character of each word + * + * @param input the string to capitalize + * @param spaceChar the character separating each word. If @code '\0' is passed, only the first character of + * the input is capitalized. + * @return the capitalized string + */ + public static String capitalize(String input, char spaceChar) { + if (spaceChar == '\0') { + return capitalize(input); + } + + char[] result = null; + boolean capitalize = true; + for (int n = input.length(), i = 0; i < n; i++) { + char c = input.charAt(i); + if (capitalize && c != (c = Character.toUpperCase(c))) { + if (result == null) result = input.toCharArray(); + result[i] = c; + } + capitalize = c == spaceChar; + } + return result != null ? String.valueOf(result) : input; + } + + public static String capitalize(String input, char spaceChar, char newSpaceChar) { + if (newSpaceChar == '\0') { + return capitalize(input, spaceChar); + } + + char[] result = null; + boolean capitalize = true; + for (int n = input.length(), i = 0; i < n; i++) { + char c = input.charAt(i); + if (capitalize && c != (c = Character.toUpperCase(c))) { + if (result == null) result = input.toCharArray(); + result[i] = c; + } + if (capitalize = c == spaceChar) { + if (result == null) result = input.toCharArray(); + result[i] = newSpaceChar; + } + } + return result != null ? String.valueOf(result) : input; + } + + /** + * Returns a lowercase version of the input with _ replaced with a space. + * Mainly used for making names of enum constants readable. + * + * @param input + * @return a humanified version of @code input + */ + public static String humanify(String input) { + return input == null ? null : input.toLowerCase().replace('_', ' '); + } + + /** + * Enumerate the given items, separating them by ", " and finally by " and " + * + * @param words the items to enumerate (it's not really enumerating....) + * @return the enumerated string + */ + public static String enumerate(String... words) { + StringBuilder result = new StringBuilder(); + int size = words.length; + int secondLastIndex = size - 2; + for (int i = 0; i < size; i++) { + String word = words[i]; + if (word.isEmpty()) + continue; + result.append(word); + if (i < secondLastIndex) + result.append(", "); + else if (i == secondLastIndex) + result.append(" and "); + } + return result.toString(); + } + + public static String enumerate(String list, String regex) { + return enumerate(list.split(regex)); + } + + /** + * Return a formatted string of the length in millis, containing days, hours and minutes. + * + * @param length The delay in milliseconds + * @return the formatted string + */ + public static String getTimeLength(long length) { + int minute = 60000; // in millis + int hour = 60 * minute; + int day = 24 * hour; + + int minutes = (int) ((length / minute) % 60); + int hours = (int) ((length / hour) % 24); + int days = (int) (length / day); //returns floor + + String result = ""; // It will be splitted at "|" + if (days != 0) + result += days + " days|"; + if (hours != 0) + result += hours + " hours|"; + if (minutes != 0) + result += minutes + " minutes|"; + return enumerate(result.split("\\|")); + } + + /** + * Return a formatted String to represent the given time length, in the given units + * + * @param sourceAmount Amount of delay + * @param sourceUnit Unit of delay + * @param ifEmpty the String to return if the + * @param displayedUnits units displayed + * @return the formatted string + * @throws IllegalArgumentException if there are no displayed units + */ + public static String getTimeLength(long sourceAmount, TimeUnit sourceUnit, String ifEmpty, TimeUnit... displayedUnits) { + if (displayedUnits.length == 0) { + throw new IllegalArgumentException("No displayed units"); + } + Arrays.sort(displayedUnits, Collections.reverseOrder(TimeUnit::compareTo)); // sort by opposite of enum declaration order (largest -> smallest) + List segments = new ArrayList<>(displayedUnits.length); + for (TimeUnit unit : displayedUnits) { + long displayedAmount = unit.convert(sourceAmount, sourceUnit); + sourceAmount -= sourceUnit.convert(displayedAmount, unit); + if (displayedAmount > 0) { + String unitWord = unit.name().toLowerCase(); // plural + if (displayedAmount == 1) { + unitWord = unitWord.substring(0, unitWord.length() - 1); // remove s at the end + } + segments.add(displayedAmount + " " + unitWord); + } + } + return segments.isEmpty() ? ifEmpty : enumerate(segments.toArray(new String[segments.size()])); + } + + /** + * Returns the delay represented by a ban-like delay representation, in milliseconds + * Example: "5d2h5m3s" for 5 days, 2 hours, 5 minutes and 3 seconds. + *

+ * Supported characters are s, m, h, d, w. + * Negative numbers are supported. + * + * @param input The input string + * @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). + */ + public static long getTimeLength(String input) { //if -1: error + long count = 0; + int i = 0; + while (i < input.length()) { + int num = 0; + char unit = '\0'; + boolean negate; + if (negate = input.charAt(i) == '-') { + i++; + } + do { + char c = input.charAt(i); + int digit = c - '0'; + if (0 <= digit && digit < 10) { + num = 10 * num + digit; + } else { + unit = c; + break; + } + } while (i < input.length()); + + long unitTime = getUnitTime(unit); + if (unitTime == -1) + throw new IllegalArgumentException(); + if (negate) { + unitTime = -unitTime; + } + count += (num * unitTime); + } + return count; + } + + /** + * Returns the time represented by the given unit character in milliseconds. + *

+ * 's' -> 1000 + * 'm' -> 1000 * 60 + * 'h' -> 1000 * 60 * 60 + * 'd' -> 1000 * 60 * 60 * 24 + * 'w' -> 1000 * 60 * 60 * 24 * 7 + * anything else -> -1 + * + * @param unit The unit character, as shown above + * @return the millisecond delay represented by the unit + */ + public static long getUnitTime(char unit) { //if -1: no value found + switch (Character.toLowerCase(unit)) { + case 's': + return 1000; + case 'm': + return 1000 * 60; + case 'h': + return 1000 * 60 * 60; + case 'd': + return 1000 * 60 * 60 * 24; + case 'w': + return 1000 * 60 * 60 * 24 * 7; + default: + return -1; + } + } + + /** + * Computes a binary representation of the value. + * The returned representation always displays 64 bits. + * Every 8 bits, the digits are seperated by an _ + * The representation is prefixed by 0b. + *

+ * Example: 0b00000000_11111111_00000001_11110000_00001111_11001100_00001111_10111010 + * + * @param entry the value to represent in binary + * @return A binary representation of the long value + */ + public static String toBinaryString(long entry) { + String binary = Long.toBinaryString(entry); + 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)); + return "0b" + withUnderscores; + } + + /** + * 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) + * @return An enum constant version of it (ENUM_FORMAT: CAPITAL_CASE) + */ + public static String toEnumFormat(String name) { + StringBuilder result = new StringBuilder(name.length() + 2); + + boolean capital = true; + for (int i = 0, n = name.length(); i < n; i++) { + char c = name.charAt(i); + if (capital) { + capital = Character.isUpperCase(c); + } else if (Character.isUpperCase(c)) { + capital = true; + result.append('_'); + } + result.append(capital ? c : Character.toUpperCase(c)); + } + + return result.toString(); + } + + /** + * 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. + * + * @param target The String to query + * @param toReplace The sequence to replace + * @param with the replacing sequence + * @return the result + */ + public static String replaceKeepColours(String target, String toReplace, String with) { + int index = -toReplace.length(); + while ((index = target.indexOf(toReplace, index + toReplace.length())) != -1) { + String start = target.substring(0, index); + Formatting coloursBefore = Formatting.getFormats(start); + String after; + try { + after = target.substring(index + toReplace.length()); + } catch (IndexOutOfBoundsException e) { + after = ""; + } + target = start + with + coloursBefore + after; + } + return target; + } + + public static String replParam(String target, String param, Object repl) { + return replParam(target, param, repl, false); + } + + public static String replParams(String target, String[] params, Object[] repls) { + return replParams(target, params, repls, false, false); + } + + public static boolean replParams(String[] target, String[] params, Object[] repls) { + return replParams(target, 0, target.length, params, repls); + } + + public static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls) { + return replParams(target, from, to, params, repls, false); + } + + public static boolean replParams(List target, String[] params, Object[] repls) { + return replParams(target, 0, target.size(), params, repls); + } + + public static boolean replParams(List target, int from, int to, String[] params, Object[] repls) { + return replParams(target, from, to, params, repls, false); + } + + public static String replParamAndTranslate(String target, String param, Object repl) { + return replParam(target, param, repl, true); + } + + public static String replParamsAndTranslate(String target, String[] params, Object[] repls) { + return replParams(target, params, repls, false, true); + } + + public static boolean replParamsAndTranslate(String[] target, String[] params, Object[] repls) { + return replParamsAndTranslate(target, 0, target.length, params, repls); + } + + public static boolean replParamsAndTranslate(String[] target, int from, int to, String[] params, Object[] repls) { + return replParams(target, from, to, params, repls, true); + } + + public static boolean replParamsAndTranslate(List target, String[] params, Object[] repls) { + return replParamsAndTranslate(target, 0, target.size(), params, repls); + } + + public static boolean replParamsAndTranslate(List target, int from, int to, String[] params, Object[] repls) { + return replParams(target, from, to, params, repls, true); + } + + private static String replParam(String target, String param, Object replacementObj, boolean translate) { + int idx = target.indexOf(param, 0); + if (idx == -1) { + return translate ? Formatting.translate(target) : target; + } + + String rep = replacementObj.toString(); + StringBuilder builder = new StringBuilder(target); + do { + builder.replace(idx, idx + param.length(), rep); + idx = builder.indexOf(param, idx + rep.length()); + } while (idx != -1); + + if (translate) { + Formatting.translate(builder); + } + + return builder.toString(); + } + + @SuppressWarnings("StringEquality") + private static boolean replParams(String[] target, int from, int to, String[] params, Object[] repls, boolean translate) { + if (from < 0 || to < from || to > target.length) { + throw new IllegalArgumentException("Invalid from-to for array size " + target.length + ": " + from + "-" + to); + } + + boolean change = false; + for (int i = from; i < to; i++) { + String val = target[i]; + if (val != (val = replParams(val, params, repls, true, translate))) { + target[i] = val; + change = true; + } + } + return change; + } + + @SuppressWarnings("StringEquality") + private static boolean replParams(List target, int from, int to, String[] params, Object[] repls, boolean translate) { + if (from < 0 || to < from || to > target.size()) { + throw new IllegalArgumentException("Invalid from-to for list size " + target.size() + ": " + from + "-" + to); + } + + boolean change = false; + if (target instanceof RandomAccess) { + for (int i = from; i < to; i++) { + String val = target.get(i); + if (val != (val = replParams(val, params, repls, true, translate))) { + target.set(i, val); + change = true; + } + } + } else { + ListIterator itr = target.listIterator(from); + for (int n = to - from, i = 0; i < n && itr.hasNext(); i++) { + String val = itr.next(); + if (val != (val = replParams(val, params, repls, true, translate))) { + itr.set(val); + change = true; + } + } + } + return change; + } + + private static String replParams(String target, String[] params, Object[] repls, boolean updateRepls, boolean translate) { + int n = params.length; + if (n != repls.length) { + throw new IllegalArgumentException(); + } + + String param = null; + int idx = -1; + int i; + for (i = 0; i < n; i++) { + param = params[i]; + if (param == null) { + continue; + } + idx = target.indexOf(param, 0); + if (idx != -1) { + break; + } + } + + if (idx == -1) { + return translate ? Formatting.translate(target) : target; + } + + String repl = repls[i].toString(); + if (updateRepls) { + repls[i] = repl; + } + + StringBuilder builder = new StringBuilder(target); + do { + builder.replace(idx, idx + param.length(), repl); + idx = builder.indexOf(param, idx + repl.length()); + } while (idx != -1); + + for (i++; i < n; i++) { + param = params[i]; + if (param == null || (idx = builder.indexOf(param, 0)) == -1) { + continue; + } + + repl = repls[i].toString(); + if (updateRepls) { + repls[i] = repl; + } + + do { + builder.replace(idx, idx + param.length(), repl); + idx = builder.indexOf(param, idx + repl.length()); + } while (idx != -1); + } + + if (translate) { + Formatting.translate(builder); + } + + return builder.toString(); + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java index 5f8f4e2..e467d91 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Whitelist.java @@ -1,91 +1,91 @@ -package io.dico.dicore; - -import org.bukkit.configuration.ConfigurationSection; - -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -@FunctionalInterface -public interface Whitelist extends Predicate { - - Whitelist EVERYTHING = item -> true; - Whitelist NOTHING = item -> false; - Whitelist NOT_NULL = Objects::nonNull; - - static Predicate everythingAsPredicate() { - return (Predicate) EVERYTHING; - } - - static Predicate nothingAsPredicate() { - return (Predicate) NOTHING; - } - - static Predicate notNullAsPredicate() { - return (Predicate) NOT_NULL; - } - - static Whitelist only(Object item) { - return item::equals; - } - - static Whitelist not(Object item) { - return o -> !item.equals(o); - } - - static Whitelist only(Object item1, Object item2) { - return item -> item1.equals(item) || item2.equals(item); - } - - static Whitelist not(Object item1, Object item2) { - return item -> !(item1.equals(item) || item2.equals(item)); - } - - static Whitelist only(Object[] objects) { - return new SetBasedWhitelist(objects, false); - } - - static Whitelist not(Object[] objects) { - return new SetBasedWhitelist(objects, true); - } - - static Whitelist fromConfig(ConfigurationSection section, Function parser) { - if (section == null) { - return NOTHING; - } - boolean blacklist = section.getBoolean("blacklist", false); - Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet()); - switch (list.size()) { - case 0: - return blacklist ? EVERYTHING : NOTHING; - case 1: { - Iterator iterator = list.iterator(); - return blacklist ? not(iterator.next()) : only(iterator.next()); - } - case 2: { - Iterator iterator = list.iterator(); - return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next()); - } - default: - Object item = list.iterator().next(); - if (item instanceof Enum) { - list = EnumSet.copyOf(list); - } - return new SetBasedWhitelist(list, blacklist); - } - } - - static void copyIntoConfig(ConfigurationSection target, Function mapper, boolean blacklist, Object... objects) { - target.set("blacklist", blacklist); - target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList())); - } - - @Override - default boolean test(Object o) { - return isWhitelisted(o); - } - - boolean isWhitelisted(Object o); -} - +package io.dico.dicore; + +import org.bukkit.configuration.ConfigurationSection; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +@FunctionalInterface +public interface Whitelist extends Predicate { + + Whitelist EVERYTHING = item -> true; + Whitelist NOTHING = item -> false; + Whitelist NOT_NULL = Objects::nonNull; + + static Predicate everythingAsPredicate() { + return (Predicate) EVERYTHING; + } + + static Predicate nothingAsPredicate() { + return (Predicate) NOTHING; + } + + static Predicate notNullAsPredicate() { + return (Predicate) NOT_NULL; + } + + static Whitelist only(Object item) { + return item::equals; + } + + static Whitelist not(Object item) { + return o -> !item.equals(o); + } + + static Whitelist only(Object item1, Object item2) { + return item -> item1.equals(item) || item2.equals(item); + } + + static Whitelist not(Object item1, Object item2) { + return item -> !(item1.equals(item) || item2.equals(item)); + } + + static Whitelist only(Object[] objects) { + return new SetBasedWhitelist(objects, false); + } + + static Whitelist not(Object[] objects) { + return new SetBasedWhitelist(objects, true); + } + + static Whitelist fromConfig(ConfigurationSection section, Function parser) { + if (section == null) { + return NOTHING; + } + boolean blacklist = section.getBoolean("blacklist", false); + Set list = section.getStringList("listed").stream().map(parser).filter(Objects::nonNull).collect(Collectors.toSet()); + switch (list.size()) { + case 0: + return blacklist ? EVERYTHING : NOTHING; + case 1: { + Iterator iterator = list.iterator(); + return blacklist ? not(iterator.next()) : only(iterator.next()); + } + case 2: { + Iterator iterator = list.iterator(); + return blacklist ? not(iterator.next(), iterator.next()) : only(iterator.next(), iterator.next()); + } + default: + Object item = list.iterator().next(); + if (item instanceof Enum) { + list = EnumSet.copyOf(list); + } + return new SetBasedWhitelist(list, blacklist); + } + } + + static void copyIntoConfig(ConfigurationSection target, Function mapper, boolean blacklist, Object... objects) { + target.set("blacklist", blacklist); + target.set("listed", Arrays.stream(objects).map(mapper).unordered().distinct().collect(Collectors.toList())); + } + + @Override + default boolean test(Object o) { + return isWhitelisted(o); + } + + boolean isWhitelisted(Object o); +} + diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java index 93584ea..378424d 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListener.java @@ -1,46 +1,46 @@ -package io.dico.dicore.event; - -import io.dico.dicore.InterfaceChain; - -public interface ChainedListener extends InterfaceChain, ChainedListener>, SimpleListener { - - @Override - default ChainedListener getEmptyInstance() { - return ChainedListeners.empty(); - } - - @Override - default ChainedListener withElement(SimpleListener element) { - if (element == null) { - return this; - } - - int count = getElementCount() + 1; - return new ChainedListener() { - @Override - public void accept(T event) { - try { - ChainedListener.this.accept(event); - } finally { - element.accept(event); - } - } - - @Override - public ChainedListener withoutLastNode() { - return ChainedListener.this; - } - - @Override - public SimpleListener getDelegateOfLastNode() { - return element; - } - - @Override - public int getElementCount() { - return count; - } - }; - } - -} +package io.dico.dicore.event; + +import io.dico.dicore.InterfaceChain; + +public interface ChainedListener extends InterfaceChain, ChainedListener>, SimpleListener { + + @Override + default ChainedListener getEmptyInstance() { + return ChainedListeners.empty(); + } + + @Override + default ChainedListener withElement(SimpleListener element) { + if (element == null) { + return this; + } + + int count = getElementCount() + 1; + return new ChainedListener() { + @Override + public void accept(T event) { + try { + ChainedListener.this.accept(event); + } finally { + element.accept(event); + } + } + + @Override + public ChainedListener withoutLastNode() { + return ChainedListener.this; + } + + @Override + public SimpleListener getDelegateOfLastNode() { + return element; + } + + @Override + public int getElementCount() { + return count; + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java index 86b8448..875c131 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandle.java @@ -1,55 +1,55 @@ -package io.dico.dicore.event; - -import io.dico.dicore.InterfaceChain; - -public interface ChainedListenerHandle extends InterfaceChain, ListenerHandle { - - @Override - default ChainedListenerHandle getEmptyInstance() { - return ChainedListenerHandles.empty(); - } - - @Override - default ChainedListenerHandle withElement(ListenerHandle element) { - if (element == null) { - return this; - } - - int count = getElementCount() + 1; - return new ChainedListenerHandle() { - @Override - public void register() { - try { - ChainedListenerHandle.this.register(); - } finally { - element.register(); - } - } - - @Override - public void unregister() { - try { - ChainedListenerHandle.this.unregister(); - } finally { - element.unregister(); - } - } - - @Override - public ChainedListenerHandle withoutLastNode() { - return ChainedListenerHandle.this; - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return element; - } - - @Override - public int getElementCount() { - return count; - } - }; - } - -} +package io.dico.dicore.event; + +import io.dico.dicore.InterfaceChain; + +public interface ChainedListenerHandle extends InterfaceChain, ListenerHandle { + + @Override + default ChainedListenerHandle getEmptyInstance() { + return ChainedListenerHandles.empty(); + } + + @Override + default ChainedListenerHandle withElement(ListenerHandle element) { + if (element == null) { + return this; + } + + int count = getElementCount() + 1; + return new ChainedListenerHandle() { + @Override + public void register() { + try { + ChainedListenerHandle.this.register(); + } finally { + element.register(); + } + } + + @Override + public void unregister() { + try { + ChainedListenerHandle.this.unregister(); + } finally { + element.unregister(); + } + } + + @Override + public ChainedListenerHandle withoutLastNode() { + return ChainedListenerHandle.this; + } + + @Override + public ListenerHandle getDelegateOfLastNode() { + return element; + } + + @Override + public int getElementCount() { + return count; + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java index 0be0508..15c4553 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListenerHandles.java @@ -1,63 +1,63 @@ -package io.dico.dicore.event; - -public class ChainedListenerHandles { - - private ChainedListenerHandles() { - - } - - private static final ChainedListenerHandle empty = new ChainedListenerHandle() { - @Override - public void register() { - - } - - public void unregister() { - - } - - @Override - public ChainedListenerHandle withElement(ListenerHandle other) { - return ChainedListenerHandles.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return null; - } - }; - - public static ChainedListenerHandle empty() { - return empty; - } - - public static ChainedListenerHandle singleton(ListenerHandle element) { - if (element instanceof ChainedListenerHandle) { - return (ChainedListenerHandle) element; - } - if (element == null) { - return empty(); - } - return new ChainedListenerHandle() { - @Override - public void register() { - element.register(); - } - - public void unregister() { - element.unregister(); - } - - @Override - public ListenerHandle getDelegateOfLastNode() { - return element; - } - }; - } - -} +package io.dico.dicore.event; + +public class ChainedListenerHandles { + + private ChainedListenerHandles() { + + } + + private static final ChainedListenerHandle empty = new ChainedListenerHandle() { + @Override + public void register() { + + } + + public void unregister() { + + } + + @Override + public ChainedListenerHandle withElement(ListenerHandle other) { + return ChainedListenerHandles.singleton(other); + } + + @Override + public int getElementCount() { + return 0; + } + + @Override + public ListenerHandle getDelegateOfLastNode() { + return null; + } + }; + + public static ChainedListenerHandle empty() { + return empty; + } + + public static ChainedListenerHandle singleton(ListenerHandle element) { + if (element instanceof ChainedListenerHandle) { + return (ChainedListenerHandle) element; + } + if (element == null) { + return empty(); + } + return new ChainedListenerHandle() { + @Override + public void register() { + element.register(); + } + + public void unregister() { + element.unregister(); + } + + @Override + public ListenerHandle getDelegateOfLastNode() { + return element; + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java index 1d2bcd4..44050a2 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ChainedListeners.java @@ -1,56 +1,56 @@ -package io.dico.dicore.event; - -@SuppressWarnings("unchecked") -public class ChainedListeners { - - private static final ChainedListener empty = new ChainedListener() { - @Override - public void accept(Object event) { - - } - - @Override - public ChainedListener withElement(SimpleListener other) { - return ChainedListeners.singleton(other); - } - - @Override - public int getElementCount() { - return 0; - } - - @Override - public SimpleListener getDelegateOfLastNode() { - return null; - } - }; - - private ChainedListeners() { - - } - - public static ChainedListener empty() { - return (ChainedListener) empty; - } - - public static ChainedListener singleton(SimpleListener element) { - if (element instanceof ChainedListener) { - return (ChainedListener) element; - } - if (element == null) { - return empty(); - } - return new ChainedListener() { - @Override - public void accept(T event) { - element.accept(event); - } - - @Override - public SimpleListener getDelegateOfLastNode() { - return element; - } - }; - } - -} +package io.dico.dicore.event; + +@SuppressWarnings("unchecked") +public class ChainedListeners { + + private static final ChainedListener empty = new ChainedListener() { + @Override + public void accept(Object event) { + + } + + @Override + public ChainedListener withElement(SimpleListener other) { + return ChainedListeners.singleton(other); + } + + @Override + public int getElementCount() { + return 0; + } + + @Override + public SimpleListener getDelegateOfLastNode() { + return null; + } + }; + + private ChainedListeners() { + + } + + public static ChainedListener empty() { + return (ChainedListener) empty; + } + + public static ChainedListener singleton(SimpleListener element) { + if (element instanceof ChainedListener) { + return (ChainedListener) element; + } + if (element == null) { + return empty(); + } + return new ChainedListener() { + @Override + public void accept(T event) { + element.accept(event); + } + + @Override + public SimpleListener getDelegateOfLastNode() { + return element; + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java index 56265bb..fa290c0 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/HandlerList.java @@ -1,91 +1,91 @@ -package io.dico.dicore.event; - -import org.bukkit.event.Cancellable; -import org.bukkit.event.EventPriority; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.function.Consumer; - -public final class HandlerList { - private final List> source = new ArrayList<>(); - private Listener[] listeners = newArray(0); - - public void refresh() { - source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal())); - listeners = source.toArray(newArray(source.size())); - } - - @SuppressWarnings("unchecked") - private static Listener[] newArray(int length) { - return new Listener[length]; - } - - public void register(Listener listener) { - if (!source.contains(listener) && source.add(listener)) { - refresh(); - } - } - - public ListenerHandle getListenerHandle(Listener listener) { - return new ListenerHandle() { - @Override - public void register() { - HandlerList.this.register(listener); - } - - @Override - public void unregister() { - HandlerList.this.unregister(listener); - } - }; - } - - public void register(EventPriority priority, Consumer listener) { - register(new Listener() { - @Override - public EventPriority getPriority() { - return priority; - } - - @Override - public void accept(T event) { - listener.accept(event); - } - }); - } - - public List> getRegistrations() { - return Collections.unmodifiableList(source); - } - - public void unregister(Listener listener) { - if (source.remove(listener)) { - refresh(); - } - } - - public void callEvent(T event) { - if (event instanceof Cancellable) { - Cancellable c = (Cancellable) event; - boolean cancelled = c.isCancelled(); - for (Listener listener : listeners) { - if (listener.listensToCancelledState(cancelled)) { - //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); - listener.accept(event); - cancelled = c.isCancelled(); - } /*else { - EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName()); - }*/ - } - } else { - for (Listener listener : listeners) { - //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); - listener.accept(event); - } - } - } - -} +package io.dico.dicore.event; + +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventPriority; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.function.Consumer; + +public final class HandlerList { + private final List> source = new ArrayList<>(); + private Listener[] listeners = newArray(0); + + public void refresh() { + source.sort(Comparator.comparingInt(l -> l.getPriority().ordinal())); + listeners = source.toArray(newArray(source.size())); + } + + @SuppressWarnings("unchecked") + private static Listener[] newArray(int length) { + return new Listener[length]; + } + + public void register(Listener listener) { + if (!source.contains(listener) && source.add(listener)) { + refresh(); + } + } + + public ListenerHandle getListenerHandle(Listener listener) { + return new ListenerHandle() { + @Override + public void register() { + HandlerList.this.register(listener); + } + + @Override + public void unregister() { + HandlerList.this.unregister(listener); + } + }; + } + + public void register(EventPriority priority, Consumer listener) { + register(new Listener() { + @Override + public EventPriority getPriority() { + return priority; + } + + @Override + public void accept(T event) { + listener.accept(event); + } + }); + } + + public List> getRegistrations() { + return Collections.unmodifiableList(source); + } + + public void unregister(Listener listener) { + if (source.remove(listener)) { + refresh(); + } + } + + public void callEvent(T event) { + if (event instanceof Cancellable) { + Cancellable c = (Cancellable) event; + boolean cancelled = c.isCancelled(); + for (Listener listener : listeners) { + if (listener.listensToCancelledState(cancelled)) { + //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); + listener.accept(event); + cancelled = c.isCancelled(); + } /*else { + EnchantsPlugin.getInstance().debug("Listener does not listen to cancelled state of " + cancelled + ": " + listener.getClass().getSimpleName()); + }*/ + } + } else { + for (Listener listener : listeners) { + //EnchantsPlugin.getInstance().debug("Listener acceptance: " + listener.getClass().getSimpleName()); + listener.accept(event); + } + } + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java index c3e5413..064cd28 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/Listener.java @@ -1,17 +1,17 @@ -package io.dico.dicore.event; - -import org.bukkit.event.EventPriority; - -public interface Listener { - - default EventPriority getPriority() { - return EventPriority.NORMAL; - } - - default boolean listensToCancelledState(boolean cancelled) { - return !cancelled; - } - - void accept(T event); - -} +package io.dico.dicore.event; + +import org.bukkit.event.EventPriority; + +public interface Listener { + + default EventPriority getPriority() { + return EventPriority.NORMAL; + } + + default boolean listensToCancelledState(boolean cancelled) { + return !cancelled; + } + + void accept(T event); + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java index 6042954..fbedd9f 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/ListenerHandle.java @@ -1,9 +1,9 @@ -package io.dico.dicore.event; - -public interface ListenerHandle { - - void register(); - - void unregister(); - -} +package io.dico.dicore.event; + +public interface ListenerHandle { + + void register(); + + void unregister(); + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java index 9688975..4e49a55 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java +++ b/dicore3/core/src/main/java/io/dico/dicore/event/SimpleListener.java @@ -1,7 +1,7 @@ -package io.dico.dicore.event; - -public interface SimpleListener { - - void accept(T event); - -} +package io.dico.dicore.event; + +public interface SimpleListener { + + void accept(T event); + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java index e95b65e..2777125 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/ExceptionHandler.java @@ -1,203 +1,203 @@ -package io.dico.dicore.exceptions; - -import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; -import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; -import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.sql.SQLException; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Supplier; - -@FunctionalInterface -public interface ExceptionHandler { - - /** - * Handle the given exception according to this handler's implementation - * - * @param ex The exception to be handled - * @throws NullPointerException if ex is null, unless the implementation specifies otherwise - * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise - */ - void handle(Throwable ex); - - /** - * Handle the given exception according to this handler's implementation - * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces. - * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)} - * - * @param ex The exception to be handled - * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces. - * @return {@code null} (unless specified otherwise by the implementation) - * @throws NullPointerException if ex is null, unless the implementation specifies otherwise - * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise - */ - default Object handleGenericException(Throwable ex, Object... args) { - handle(ex); - return null; - } - - /** - * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is never expected to throw - * an unchecked exception other than {@link Error} - */ - default boolean isSafe() { - return true; - } - - /** - * Runs the given checked action, handling any thrown exceptions using this exception handler. - *

- * Any exceptions thrown by this handler are delegated to the caller. - * - * @param action The action to run - * @throws NullPointerException if action is null - */ - default void runSafe(CheckedRunnable action) { - Objects.requireNonNull(action); - try { - action.checkedRun(); - } catch (Throwable ex) { - handle(ex); - } - } - - /** - * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler. - *

- * Any exceptions thrown by this handler are delegated to the caller. - * - * @param action The supplier whose result to compute - * @param generic type parameter for the supplier and the result type of this method - * @return The result of this computation, or null if an error occurred - * @throws NullPointerException if action is null - */ - - default T supplySafe(CheckedSupplier action) { - Objects.requireNonNull(action); - try { - return action.checkedGet(); - } catch (Throwable ex) { - handle(ex); - return null; - } - } - - /** - * @param action The action to wrap - * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method. - * @see #runSafe(CheckedRunnable) - */ - default Runnable safeRunnable(CheckedRunnable action) { - return () -> runSafe(action); - } - - /** - * @param supplier The computation to wrap - * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method. - * @see #supplySafe(CheckedSupplier) - */ - default Supplier safeSupplier(CheckedSupplier supplier) { - return () -> supplySafe(supplier); - } - - /** - * Logs the given exception as an error to {@code out} - *

- * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace - * - * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}. - * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown - * @param ex The exception that was thrown - * @throws NullPointerException if any argument is null - */ - static void log(Consumer out, String failedActivityDescription, Throwable ex) { - if (out == null || failedActivityDescription == null || ex == null) { - throw new NullPointerException(); - } - - StringWriter msg = new StringWriter(1024); - msg.append("Error occurred while ").append(failedActivityDescription).append(':'); - - if (ex instanceof SQLException) { - SQLException sqlex = (SQLException) ex; - msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode())); - msg.append('\n').append("SQL State: ").append(sqlex.getSQLState()); - } - - msg.append('\n').append("=======START STACK======="); - try (PrintWriter pw = new PrintWriter(msg)) { - ex.printStackTrace(pw); - } - msg.append('\n').append("========END STACK========"); - - out.accept(msg.toString()); - } - - /** - * @param activityDescription The activity description - * @return An ExceptionHandler that prints to {@link System#out} - * @see #log(Consumer, String) - */ - static ExceptionHandler log(String activityDescription) { - // A method reference would cache the current value in System.out - // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours). - return log(msg -> System.out.println(msg), activityDescription); - } - - /** - * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)} - * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)} - * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)} - * @see #log(Consumer, String, Throwable) - */ - static ExceptionHandler log(Consumer out, String activityDescription) { - return ex -> log(out, activityDescription, ex); - } - - /** - * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again. - */ - ExceptionHandler UNCHECKED = new ExceptionHandler() { - @Override - public void handle(Throwable ex) { - errorFilter(ex); - if (ex instanceof RuntimeException) { - throw (RuntimeException) ex; - } - throw new RuntimeException(ex); - } - - @Override - public boolean isSafe() { - return false; - } - }; - - /** - * This ExceptionHandler suppresses all exceptions, - * apart from {@link Error} because that is not usually supposed to be caught - */ - ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter; - - /** - * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}. - */ - ExceptionHandler PRINT_UNLESS_NP = ex -> { - errorFilter(ex); - if (!(ex instanceof NullPointerException)) { - ex.printStackTrace(); - } - }; - - static void errorFilter(Throwable ex) { - if (ex instanceof Error) { - throw (Error) ex; - } else if (ex == null) { - throw new NullPointerException(); - } - } - -} +package io.dico.dicore.exceptions; + +import io.dico.dicore.exceptions.checkedfunctions.CheckedRunnable; +import io.dico.dicore.exceptions.checkedfunctions.CheckedSupplier; +import io.dico.dicore.exceptions.checkedfunctions.CheckedFunctionalObject; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.sql.SQLException; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@FunctionalInterface +public interface ExceptionHandler { + + /** + * Handle the given exception according to this handler's implementation + * + * @param ex The exception to be handled + * @throws NullPointerException if ex is null, unless the implementation specifies otherwise + * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise + */ + void handle(Throwable ex); + + /** + * Handle the given exception according to this handler's implementation + * This method is intended for use by {@link CheckedFunctionalObject} and subinterfaces. + * It supplies exception handlers the option to acquire more information, by overriding this method and calling it from {@link #handle(Throwable)} + * + * @param ex The exception to be handled + * @param args Any arguments passed, this is used by {@link CheckedFunctionalObject} and subinterfaces. + * @return {@code null} (unless specified otherwise by the implementation) + * @throws NullPointerException if ex is null, unless the implementation specifies otherwise + * @throws Error ex if ex is an instance of Error, unless the implementation specifies otherwise + */ + default Object handleGenericException(Throwable ex, Object... args) { + handle(ex); + return null; + } + + /** + * @return true if this {@link ExceptionHandler}'s {@link #handleGenericException(Throwable, Object...)} method is never expected to throw + * an unchecked exception other than {@link Error} + */ + default boolean isSafe() { + return true; + } + + /** + * Runs the given checked action, handling any thrown exceptions using this exception handler. + *

+ * Any exceptions thrown by this handler are delegated to the caller. + * + * @param action The action to run + * @throws NullPointerException if action is null + */ + default void runSafe(CheckedRunnable action) { + Objects.requireNonNull(action); + try { + action.checkedRun(); + } catch (Throwable ex) { + handle(ex); + } + } + + /** + * Computes the result of the given checked supplier, handling any thrown exceptions using this exception handler. + *

+ * Any exceptions thrown by this handler are delegated to the caller. + * + * @param action The supplier whose result to compute + * @param generic type parameter for the supplier and the result type of this method + * @return The result of this computation, or null if an error occurred + * @throws NullPointerException if action is null + */ + + default T supplySafe(CheckedSupplier action) { + Objects.requireNonNull(action); + try { + return action.checkedGet(); + } catch (Throwable ex) { + handle(ex); + return null; + } + } + + /** + * @param action The action to wrap + * @return A runnable that wraps the given action using this handler's {@link #runSafe(CheckedRunnable)} method. + * @see #runSafe(CheckedRunnable) + */ + default Runnable safeRunnable(CheckedRunnable action) { + return () -> runSafe(action); + } + + /** + * @param supplier The computation to wrap + * @return A supplier that wraps the given computation using this handler's {@link #supplySafe(CheckedSupplier)} method. + * @see #supplySafe(CheckedSupplier) + */ + default Supplier safeSupplier(CheckedSupplier supplier) { + return () -> supplySafe(supplier); + } + + /** + * Logs the given exception as an error to {@code out} + *

+ * Format: Error occurred while {@code failedActivityDescription}, followed by additional details and a stack trace + * + * @param out The consumer to accept the error message, for instance {@code {@link java.util.logging.Logger logger}::warning}. + * @param failedActivityDescription A description of the activity that was being executed when the exception was thrown + * @param ex The exception that was thrown + * @throws NullPointerException if any argument is null + */ + static void log(Consumer out, String failedActivityDescription, Throwable ex) { + if (out == null || failedActivityDescription == null || ex == null) { + throw new NullPointerException(); + } + + StringWriter msg = new StringWriter(1024); + msg.append("Error occurred while ").append(failedActivityDescription).append(':'); + + if (ex instanceof SQLException) { + SQLException sqlex = (SQLException) ex; + msg.append('\n').append("Error code: ").append(Integer.toString(sqlex.getErrorCode())); + msg.append('\n').append("SQL State: ").append(sqlex.getSQLState()); + } + + msg.append('\n').append("=======START STACK======="); + try (PrintWriter pw = new PrintWriter(msg)) { + ex.printStackTrace(pw); + } + msg.append('\n').append("========END STACK========"); + + out.accept(msg.toString()); + } + + /** + * @param activityDescription The activity description + * @return An ExceptionHandler that prints to {@link System#out} + * @see #log(Consumer, String) + */ + static ExceptionHandler log(String activityDescription) { + // A method reference would cache the current value in System.out + // This would update if the value in System.out is changed (by for example, applying a PrintStream that handles colours). + return log(msg -> System.out.println(msg), activityDescription); + } + + /** + * @param out The Consumer to be passed to {@link #log(Consumer, String, Throwable)} + * @param activityDescription The activity description to be passed to {@link #log(Consumer, String, Throwable)} + * @return An ExceptionHandler that passes exceptions with given arguments to {@link #log(Consumer, String, Throwable)} + * @see #log(Consumer, String, Throwable) + */ + static ExceptionHandler log(Consumer out, String activityDescription) { + return ex -> log(out, activityDescription, ex); + } + + /** + * This ExceptionHandler turns any Throwable into an unchecked exception, then throws it again. + */ + ExceptionHandler UNCHECKED = new ExceptionHandler() { + @Override + public void handle(Throwable ex) { + errorFilter(ex); + if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; + } + throw new RuntimeException(ex); + } + + @Override + public boolean isSafe() { + return false; + } + }; + + /** + * This ExceptionHandler suppresses all exceptions, + * apart from {@link Error} because that is not usually supposed to be caught + */ + ExceptionHandler SUPPRESS = ExceptionHandler::errorFilter; + + /** + * This ExceptionHandler calls {@link Throwable#printStackTrace()} unless it is a {@link NullPointerException} or {@link Error}. + */ + ExceptionHandler PRINT_UNLESS_NP = ex -> { + errorFilter(ex); + if (!(ex instanceof NullPointerException)) { + ex.printStackTrace(); + } + }; + + static void errorFilter(Throwable ex) { + if (ex instanceof Error) { + throw (Error) ex; + } else if (ex == null) { + throw new NullPointerException(); + } + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java index badf05b..bfcafe8 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiConsumer.java @@ -1,69 +1,69 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiConsumer; - -/** - * checked mimic of {@link BiConsumer} - * - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedBiConsumer - extends CheckedFunctionalObject, BiConsumer { - - /** - * The consuming action - * - * @param t the first argument to consume - * @param u the second argument to consume - * @throws TException if an exception occurs - */ - void checkedAccept(TParam1 t, TParam2 u) throws TException; - - /** - * unchecked wrapper for {@link #checkedAccept(Object, Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the first input - * @param u the second input - * @see #checkedAccept(Object, Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void accept(TParam1 t, TParam2 u) { - try { - checkedAccept(t, u); - } catch (Throwable ex) { - handleGenericException(ex, t, u); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedBiConsumer handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiConsumer() { - @Override - public void checkedAccept(TParam1 t, TParam2 u) throws TException { - CheckedBiConsumer.this.checkedAccept(t, u); - } - - @Override - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedBiConsumer handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.util.function.BiConsumer; + +/** + * checked mimic of {@link BiConsumer} + * + * @param + * @param + * @param + */ +@FunctionalInterface +public interface CheckedBiConsumer + extends CheckedFunctionalObject, BiConsumer { + + /** + * The consuming action + * + * @param t the first argument to consume + * @param u the second argument to consume + * @throws TException if an exception occurs + */ + void checkedAccept(TParam1 t, TParam2 u) throws TException; + + /** + * unchecked wrapper for {@link #checkedAccept(Object, Object)} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @param t the first input + * @param u the second input + * @see #checkedAccept(Object, Object) + * @see #resultOnError(Throwable, Object...) + */ + @Override + default void accept(TParam1 t, TParam2 u) { + try { + checkedAccept(t, u); + } catch (Throwable ex) { + handleGenericException(ex, t, u); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedBiConsumer handleExceptionsWith(ExceptionHandler handler) { + return new CheckedBiConsumer() { + @Override + public void checkedAccept(TParam1 t, TParam2 u) throws TException { + CheckedBiConsumer.this.checkedAccept(t, u); + } + + @Override + public Void handleGenericException(Throwable thrown, Object... args) { + handler.handleGenericException(thrown, args); + return null; + } + + @Override + public CheckedBiConsumer handleExceptionsWith(ExceptionHandler handler) { + return CheckedBiConsumer.this.handleExceptionsWith(handler); + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java index 9b39325..11fca3f 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedBiFunction.java @@ -1,76 +1,76 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.BiFunction; - -/** - * checked mimic of {@link BiFunction} - * - * @param - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedBiFunction - extends CheckedFunctionalObject, BiFunction { - - /** - * the functional method - * - * @param t the first input - * @param u the second input - * @return the function output - * @throws TException if an exception occurs - */ - TResult checkedApply(TParam1 t, TParam2 u) throws TException; - - /** - * unchecked wrapper for {@link #checkedApply(Object, Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the first input - * @param u the second input - * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown - * @see #checkedApply(Object, Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult apply(TParam1 t, TParam2 u) { - try { - return checkedApply(t, u); - } catch (Throwable ex) { - return handleGenericException(ex, t, u); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedBiFunction handleExceptionsWith(ExceptionHandler handler) { - return new CheckedBiFunction() { - @Override - public TResult checkedApply(TParam1 t, TParam2 u) throws TException { - return CheckedBiFunction.this.checkedApply(t, u); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedBiFunction handleExceptionsWith(ExceptionHandler handler) { - return CheckedBiFunction.this.handleExceptionsWith(handler); - } - }; - } -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.util.function.BiFunction; + +/** + * checked mimic of {@link BiFunction} + * + * @param + * @param + * @param + * @param + */ +@FunctionalInterface +public interface CheckedBiFunction + extends CheckedFunctionalObject, BiFunction { + + /** + * the functional method + * + * @param t the first input + * @param u the second input + * @return the function output + * @throws TException if an exception occurs + */ + TResult checkedApply(TParam1 t, TParam2 u) throws TException; + + /** + * unchecked wrapper for {@link #checkedApply(Object, Object)} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @param t the first input + * @param u the second input + * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown + * @see #checkedApply(Object, Object) + * @see #resultOnError(Throwable, Object...) + */ + @Override + default TResult apply(TParam1 t, TParam2 u) { + try { + return checkedApply(t, u); + } catch (Throwable ex) { + return handleGenericException(ex, t, u); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedBiFunction handleExceptionsWith(ExceptionHandler handler) { + return new CheckedBiFunction() { + @Override + public TResult checkedApply(TParam1 t, TParam2 u) throws TException { + return CheckedBiFunction.this.checkedApply(t, u); + } + + @Override + @SuppressWarnings("unchecked") + public TResult handleGenericException(Throwable thrown, Object... args) { + Object result = handler.handleGenericException(thrown, args); + try { + return (TResult) result; + } catch (Exception ex) { + return null; + } + } + + @Override + public CheckedBiFunction handleExceptionsWith(ExceptionHandler handler) { + return CheckedBiFunction.this.handleExceptionsWith(handler); + } + }; + } +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java index fa48ae8..abe6d53 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedConsumer.java @@ -1,67 +1,67 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Consumer; - -/** - * checked mimic of {@link Consumer} - * - * @param - * @param - */ -@FunctionalInterface -public interface CheckedConsumer - extends CheckedFunctionalObject, Consumer { - - /** - * The consuming action - * - * @param t the argument to consume - * @throws TException if an error occurs - */ - void checkedAccept(TParam t) throws TException; - - /** - * Unchecked version of {@link #checkedAccept(Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the argument to consume - * @see #checkedAccept(Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void accept(TParam t) { - try { - checkedAccept(t); - } catch (Throwable ex) { - handleGenericException(ex, t); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedConsumer handleExceptionsWith(ExceptionHandler handler) { - return new CheckedConsumer() { - @Override - public void checkedAccept(TParam t) throws TException { - CheckedConsumer.this.checkedAccept(t); - } - - @Override - @SuppressWarnings("unchecked") - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedConsumer handleExceptionsWith(ExceptionHandler handler) { - return CheckedConsumer.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.util.function.Consumer; + +/** + * checked mimic of {@link Consumer} + * + * @param + * @param + */ +@FunctionalInterface +public interface CheckedConsumer + extends CheckedFunctionalObject, Consumer { + + /** + * The consuming action + * + * @param t the argument to consume + * @throws TException if an error occurs + */ + void checkedAccept(TParam t) throws TException; + + /** + * Unchecked version of {@link #checkedAccept(Object)} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @param t the argument to consume + * @see #checkedAccept(Object) + * @see #resultOnError(Throwable, Object...) + */ + @Override + default void accept(TParam t) { + try { + checkedAccept(t); + } catch (Throwable ex) { + handleGenericException(ex, t); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedConsumer handleExceptionsWith(ExceptionHandler handler) { + return new CheckedConsumer() { + @Override + public void checkedAccept(TParam t) throws TException { + CheckedConsumer.this.checkedAccept(t); + } + + @Override + @SuppressWarnings("unchecked") + public Void handleGenericException(Throwable thrown, Object... args) { + handler.handleGenericException(thrown, args); + return null; + } + + @Override + public CheckedConsumer handleExceptionsWith(ExceptionHandler handler) { + return CheckedConsumer.this.handleExceptionsWith(handler); + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java index 9ff806a..48ac207 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunction.java @@ -1,74 +1,74 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Function; - -/** - * checked mimic of {@link Function} - * - * @param - * @param - * @param - */ -@FunctionalInterface -public interface CheckedFunction - extends CheckedFunctionalObject, Function { - - /** - * the functional method - * - * @param t the input - * @return the function output - * @throws TException if an exception occurs - */ - TResult checkedApply(TParam t) throws TException; - - /** - * unchecked wrapper for {@link #checkedApply(Object)} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @param t the input - * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown - * @see #checkedApply(Object) - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult apply(TParam t) { - try { - return checkedApply(t); - } catch (Throwable ex) { - return handleGenericException(ex, t); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedFunction handleExceptionsWith(ExceptionHandler handler) { - return new CheckedFunction() { - @Override - public TResult checkedApply(TParam t) throws TException { - return CheckedFunction.this.checkedApply(t); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedFunction handleExceptionsWith(ExceptionHandler handler) { - return CheckedFunction.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.util.function.Function; + +/** + * checked mimic of {@link Function} + * + * @param + * @param + * @param + */ +@FunctionalInterface +public interface CheckedFunction + extends CheckedFunctionalObject, Function { + + /** + * the functional method + * + * @param t the input + * @return the function output + * @throws TException if an exception occurs + */ + TResult checkedApply(TParam t) throws TException; + + /** + * unchecked wrapper for {@link #checkedApply(Object)} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @param t the input + * @return the function output, or the result from {@link #resultOnError(Throwable, Object...)} if an exception was thrown + * @see #checkedApply(Object) + * @see #resultOnError(Throwable, Object...) + */ + @Override + default TResult apply(TParam t) { + try { + return checkedApply(t); + } catch (Throwable ex) { + return handleGenericException(ex, t); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedFunction handleExceptionsWith(ExceptionHandler handler) { + return new CheckedFunction() { + @Override + public TResult checkedApply(TParam t) throws TException { + return CheckedFunction.this.checkedApply(t); + } + + @Override + @SuppressWarnings("unchecked") + public TResult handleGenericException(Throwable thrown, Object... args) { + Object result = handler.handleGenericException(thrown, args); + try { + return (TResult) result; + } catch (Exception ex) { + return null; + } + } + + @Override + public CheckedFunction handleExceptionsWith(ExceptionHandler handler) { + return CheckedFunction.this.handleExceptionsWith(handler); + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java index e84b33d..d9d7a53 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedFunctionalObject.java @@ -1,85 +1,85 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -/** - * Base interface for all checked functional interfaces - * Most subinterfaces will mimic interfaces in the package {@link java.util.function} - *

- * Checked functional interfaces are functions with throws declarations. - * The name comes from the fact that they can throw checked exceptions. - *

- * They extend their non-checked counterparts, whose methods are implemented by - * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown - *

- * Made public to allow more specialized checked functional interfaces to subclass it. - * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions, - * the performance impact of which is probably a few magnitudes larger. Don't quote me on this. - * - * @param The return type of this functional interface's method - * @param The type of exception that might be thrown - */ -public interface CheckedFunctionalObject extends ExceptionHandler { - - /** - * {@inheritDoc} - * - * @param ex The exception to be handled - */ - @Override - default void handle(Throwable ex) { - handleGenericException(ex); - } - - /** - * {@inheritDoc} - *

- * Method to handle exceptions thrown by the default implementations of subinterfaces. - * Since you can't catch a type parameter as exception, this code is in place to take care of it. - *

- * If the thrown exception is not a TException, an unchecked version is thrown by calling this method. - * - * @param thrown The thrown exception - * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present. - * @return The result computed by {@link #resultOnError(Throwable, Object...)} - * @see #resultOnError(Throwable, Object...) - */ - @Override - @SuppressWarnings("unchecked") - default TResult handleGenericException(Throwable thrown, Object... args) { - - // check if the throwable is a TException - TException castedException; - try { - castedException = (TException) thrown; - } catch (ClassCastException ex) { - // if not, throw an unchecked version of it - ExceptionHandler.UNCHECKED.handleGenericException(thrown); - // this code is never reached. - return null; - } - - // if it is a TException, use resultOnError to compute result. - return resultOnError(castedException, args); - } - - /** - * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper. - * - * @param ex The exception thrown - * @param args The (typed) arguments passed, if any - * @return The result to return, if any - */ - default TResult resultOnError(TException ex, Object... args) { - return null; - } - - /** - * Creates a new functional object that uses the given exception handler to handle exceptions. - * - * @param handler The handler to handle exceptions - * @return a new functional object that uses the given exception handler to handle exceptions - */ - CheckedFunctionalObject handleExceptionsWith(ExceptionHandler handler); - -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +/** + * Base interface for all checked functional interfaces + * Most subinterfaces will mimic interfaces in the package {@link java.util.function} + *

+ * Checked functional interfaces are functions with throws declarations. + * The name comes from the fact that they can throw checked exceptions. + *

+ * They extend their non-checked counterparts, whose methods are implemented by + * returning the result of {@link #resultOnError(TException, Object...)} when an exception is thrown + *

+ * Made public to allow more specialized checked functional interfaces to subclass it. + * Making primitive versions shouldn't provide a significant performance increase because we're checking for exceptions, + * the performance impact of which is probably a few magnitudes larger. Don't quote me on this. + * + * @param The return type of this functional interface's method + * @param The type of exception that might be thrown + */ +public interface CheckedFunctionalObject extends ExceptionHandler { + + /** + * {@inheritDoc} + * + * @param ex The exception to be handled + */ + @Override + default void handle(Throwable ex) { + handleGenericException(ex); + } + + /** + * {@inheritDoc} + *

+ * Method to handle exceptions thrown by the default implementations of subinterfaces. + * Since you can't catch a type parameter as exception, this code is in place to take care of it. + *

+ * If the thrown exception is not a TException, an unchecked version is thrown by calling this method. + * + * @param thrown The thrown exception + * @param args the arguments supplied to the method that threw the exception, if any. These are not guaranteed to be given if parameters are present. + * @return The result computed by {@link #resultOnError(Throwable, Object...)} + * @see #resultOnError(Throwable, Object...) + */ + @Override + @SuppressWarnings("unchecked") + default TResult handleGenericException(Throwable thrown, Object... args) { + + // check if the throwable is a TException + TException castedException; + try { + castedException = (TException) thrown; + } catch (ClassCastException ex) { + // if not, throw an unchecked version of it + ExceptionHandler.UNCHECKED.handleGenericException(thrown); + // this code is never reached. + return null; + } + + // if it is a TException, use resultOnError to compute result. + return resultOnError(castedException, args); + } + + /** + * This method handles the exceptions thrown by the checked method of this object, when called by its unchecked wrapper. + * + * @param ex The exception thrown + * @param args The (typed) arguments passed, if any + * @return The result to return, if any + */ + default TResult resultOnError(TException ex, Object... args) { + return null; + } + + /** + * Creates a new functional object that uses the given exception handler to handle exceptions. + * + * @param handler The handler to handle exceptions + * @return a new functional object that uses the given exception handler to handle exceptions + */ + CheckedFunctionalObject handleExceptionsWith(ExceptionHandler handler); + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java index 55de6f8..110c988 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedRunnable.java @@ -1,62 +1,62 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -/** - * checked mimic of {@link Runnable} - * - * @param - */ -@FunctionalInterface -public interface CheckedRunnable - extends CheckedFunctionalObject, Runnable { - - /** - * The runnable action - * - * @throws TException if an exception occurs - */ - void checkedRun() throws TException; - - /** - * Unchecked version of {@link #checkedRun()} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @see #checkedRun() - * @see #resultOnError(Throwable, Object...) - */ - @Override - default void run() { - try { - checkedRun(); - } catch (Throwable ex) { - handleGenericException(ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedRunnable handleExceptionsWith(ExceptionHandler handler) { - return new CheckedRunnable() { - @Override - public void checkedRun() throws TException { - CheckedRunnable.this.checkedRun(); - } - - @Override - @SuppressWarnings("unchecked") - public Void handleGenericException(Throwable thrown, Object... args) { - handler.handleGenericException(thrown, args); - return null; - } - - @Override - public CheckedRunnable handleExceptionsWith(ExceptionHandler handler) { - return CheckedRunnable.this.handleExceptionsWith(handler); - } - }; - } - -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +/** + * checked mimic of {@link Runnable} + * + * @param + */ +@FunctionalInterface +public interface CheckedRunnable + extends CheckedFunctionalObject, Runnable { + + /** + * The runnable action + * + * @throws TException if an exception occurs + */ + void checkedRun() throws TException; + + /** + * Unchecked version of {@link #checkedRun()} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @see #checkedRun() + * @see #resultOnError(Throwable, Object...) + */ + @Override + default void run() { + try { + checkedRun(); + } catch (Throwable ex) { + handleGenericException(ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedRunnable handleExceptionsWith(ExceptionHandler handler) { + return new CheckedRunnable() { + @Override + public void checkedRun() throws TException { + CheckedRunnable.this.checkedRun(); + } + + @Override + @SuppressWarnings("unchecked") + public Void handleGenericException(Throwable thrown, Object... args) { + handler.handleGenericException(thrown, args); + return null; + } + + @Override + public CheckedRunnable handleExceptionsWith(ExceptionHandler handler) { + return CheckedRunnable.this.handleExceptionsWith(handler); + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java index dec2e7e..7820428 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java +++ b/dicore3/core/src/main/java/io/dico/dicore/exceptions/checkedfunctions/CheckedSupplier.java @@ -1,70 +1,70 @@ -package io.dico.dicore.exceptions.checkedfunctions; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.util.function.Supplier; - -/** - * checked mimic of {@link Supplier} - * - * @param - * @param - */ -@FunctionalInterface -public interface CheckedSupplier - extends CheckedFunctionalObject, Supplier { - - /** - * The computation - * - * @return the result of this computation - * @throws TException if an error occurs - */ - TResult checkedGet() throws TException; - - /** - * Unchecked version of {@link #checkedGet()} - * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} - * - * @return the result of this computation - * @see #checkedGet() - * @see #resultOnError(Throwable, Object...) - */ - @Override - default TResult get() { - try { - return checkedGet(); - } catch (Throwable ex) { - return handleGenericException(ex); - } - } - - /** - * {@inheritDoc} - */ - @Override - default CheckedSupplier handleExceptionsWith(ExceptionHandler handler) { - return new CheckedSupplier() { - @Override - public TResult checkedGet() throws TException { - return CheckedSupplier.this.checkedGet(); - } - - @Override - @SuppressWarnings("unchecked") - public TResult handleGenericException(Throwable thrown, Object... args) { - Object result = handler.handleGenericException(thrown, args); - try { - return (TResult) result; - } catch (Exception ex) { - return null; - } - } - - @Override - public CheckedSupplier handleExceptionsWith(ExceptionHandler handler) { - return CheckedSupplier.this.handleExceptionsWith(handler); - } - }; - } -} +package io.dico.dicore.exceptions.checkedfunctions; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.util.function.Supplier; + +/** + * checked mimic of {@link Supplier} + * + * @param + * @param + */ +@FunctionalInterface +public interface CheckedSupplier + extends CheckedFunctionalObject, Supplier { + + /** + * The computation + * + * @return the result of this computation + * @throws TException if an error occurs + */ + TResult checkedGet() throws TException; + + /** + * Unchecked version of {@link #checkedGet()} + * If a {@link TException} occurs, an unchecked one might be thrown by {@link #resultOnError(Throwable, Object...)} + * + * @return the result of this computation + * @see #checkedGet() + * @see #resultOnError(Throwable, Object...) + */ + @Override + default TResult get() { + try { + return checkedGet(); + } catch (Throwable ex) { + return handleGenericException(ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + default CheckedSupplier handleExceptionsWith(ExceptionHandler handler) { + return new CheckedSupplier() { + @Override + public TResult checkedGet() throws TException { + return CheckedSupplier.this.checkedGet(); + } + + @Override + @SuppressWarnings("unchecked") + public TResult handleGenericException(Throwable thrown, Object... args) { + Object result = handler.handleGenericException(thrown, args); + try { + return (TResult) result; + } catch (Exception ex) { + return null; + } + } + + @Override + public CheckedSupplier handleExceptionsWith(ExceptionHandler handler) { + return CheckedSupplier.this.handleExceptionsWith(handler); + } + }; + } +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java index b400cce..43db459 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/BaseTask.java @@ -1,108 +1,108 @@ -package io.dico.dicore.task; - -import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; - -import java.util.NoSuchElementException; - -public abstract class BaseTask { - private boolean running = false; - private Integer taskId = null; - private long workTime = 5L; - private int workCount; - - public void start(Plugin plugin, int delay, int period, long workTime) { - doStartChecks(); - this.workTime = workTime; - workCount = 0; - running = true; - - if (delay == -1) { - run(); - if (!running) { - return; - } - delay = period; - } - - taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period); - } - - public void startImmediately(Plugin plugin, int period, long workTime) { - start(plugin, -1, period, workTime); - } - - protected void doStartChecks() { - if (isRunning()) { - throw new IllegalStateException("Can't start when already running"); - } - } - - public void start(Plugin plugin) { - start(plugin, -1, 20, 5L); - } - - protected void onFinish(boolean early) { - } - - protected long getWorkTime() { - return workTime; - } - - protected abstract boolean process(T object); - - private void run() { - workCount++; - final long stop = System.currentTimeMillis() + getWorkTime(); - do { - if (!processNext()) { - return; - } - } while (System.currentTimeMillis() < stop); - } - - public int getTaskId() { - return running ? taskId : -1; - } - - public int getWorkCount() { - return workCount; - } - - public boolean isRunning() { - return running; - } - - protected abstract T supply() throws NoSuchElementException; - - private void cancelTask(boolean early) { - if (taskId != null) { - Bukkit.getScheduler().cancelTask(taskId); - } - running = false; - taskId = null; - onFinish(early); - } - - private boolean processNext() { - T object; - try { - object = supply(); - } catch (NoSuchElementException e) { - cancelTask(false); - return false; - } - - try { - if (process(object)) { - return true; - } - } catch (RuntimeException e) { - e.printStackTrace(); - } - - cancelTask(true); - return false; - } - -} +package io.dico.dicore.task; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import java.util.NoSuchElementException; + +public abstract class BaseTask { + private boolean running = false; + private Integer taskId = null; + private long workTime = 5L; + private int workCount; + + public void start(Plugin plugin, int delay, int period, long workTime) { + doStartChecks(); + this.workTime = workTime; + workCount = 0; + running = true; + + if (delay == -1) { + run(); + if (!running) { + return; + } + delay = period; + } + + taskId = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, this::run, delay, period); + } + + public void startImmediately(Plugin plugin, int period, long workTime) { + start(plugin, -1, period, workTime); + } + + protected void doStartChecks() { + if (isRunning()) { + throw new IllegalStateException("Can't start when already running"); + } + } + + public void start(Plugin plugin) { + start(plugin, -1, 20, 5L); + } + + protected void onFinish(boolean early) { + } + + protected long getWorkTime() { + return workTime; + } + + protected abstract boolean process(T object); + + private void run() { + workCount++; + final long stop = System.currentTimeMillis() + getWorkTime(); + do { + if (!processNext()) { + return; + } + } while (System.currentTimeMillis() < stop); + } + + public int getTaskId() { + return running ? taskId : -1; + } + + public int getWorkCount() { + return workCount; + } + + public boolean isRunning() { + return running; + } + + protected abstract T supply() throws NoSuchElementException; + + private void cancelTask(boolean early) { + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + running = false; + taskId = null; + onFinish(early); + } + + private boolean processNext() { + T object; + try { + object = supply(); + } catch (NoSuchElementException e) { + cancelTask(false); + return false; + } + + try { + if (process(object)) { + return true; + } + } catch (RuntimeException e) { + e.printStackTrace(); + } + + cancelTask(true); + return false; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java index e58814c..3ff7be7 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/IteratorTask.java @@ -1,120 +1,120 @@ -package io.dico.dicore.task; - -import java.util.*; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public abstract class IteratorTask extends BaseTask { - - private Iterator iterator; - - public IteratorTask() { - } - - @SuppressWarnings("unchecked") - public IteratorTask(Iterable iterable, boolean clone) { - refresh(iterable, clone); - } - - public IteratorTask(Iterable iterable) { - this(iterable, false); - } - - public IteratorTask(Iterator iterator) { - refresh(iterator); - } - - @Override - protected void doStartChecks() { - super.doStartChecks(); - if (iterator == null) { - throw new IllegalStateException("An iterator must be supplied first"); - } - } - - protected final void refresh(Iterable iterable, boolean clone) { - if (clone) { - Collection collection; - if (!(iterable instanceof Collection)) { - collection = new LinkedList<>(); - for (T next : iterable) { - collection.add(next); - } - } else { - collection = new ArrayList((Collection) iterable); - } - iterator = collection.iterator(); - } else { - iterator = iterable.iterator(); - } - } - - protected final void refresh(Iterator iterator) { - Objects.requireNonNull(iterator); - this.iterator = iterator; - } - - @Override - protected T supply() { - return iterator.next(); - } - - protected void remove() { - iterator.remove(); - } - - // One argument: The processed object - - public static IteratorTask create(Iterable iterable, Consumer processor) { - return create(iterable, false, processor); - } - - public static IteratorTask create(Iterable iterable, boolean clone, Consumer processor) { - return create(iterable, clone, object -> { - processor.accept(object); - return true; - }); - } - - public static IteratorTask create(Iterable iterable, Predicate processor) { - return create(iterable, false, processor); - } - - public static IteratorTask create(Iterable iterable, boolean clone, Predicate processor) { - return new IteratorTask(iterable, clone) { - @Override - protected boolean process(T object) { - return processor.test(object); - } - }; - } - - // Two arguments: the processed object, and a runnable to remove it from the iterator. - - public static IteratorTask create(Iterable iterable, BiConsumer processor) { - return create(iterable, false, processor); - } - - public static IteratorTask create(Iterable iterable, boolean clone, BiConsumer processor) { - return create(iterable, clone, (object, runnable) -> { - processor.accept(object, runnable); - return true; - }); - } - - public static IteratorTask create(Iterable iterable, BiPredicate processor) { - return create(iterable, false, processor); - } - - public static IteratorTask create(Iterable iterable, boolean clone, BiPredicate processor) { - return new IteratorTask(iterable, clone) { - @Override - protected boolean process(T object) { - return processor.test(object, this::remove); - } - }; - } - -} +package io.dico.dicore.task; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public abstract class IteratorTask extends BaseTask { + + private Iterator iterator; + + public IteratorTask() { + } + + @SuppressWarnings("unchecked") + public IteratorTask(Iterable iterable, boolean clone) { + refresh(iterable, clone); + } + + public IteratorTask(Iterable iterable) { + this(iterable, false); + } + + public IteratorTask(Iterator iterator) { + refresh(iterator); + } + + @Override + protected void doStartChecks() { + super.doStartChecks(); + if (iterator == null) { + throw new IllegalStateException("An iterator must be supplied first"); + } + } + + protected final void refresh(Iterable iterable, boolean clone) { + if (clone) { + Collection collection; + if (!(iterable instanceof Collection)) { + collection = new LinkedList<>(); + for (T next : iterable) { + collection.add(next); + } + } else { + collection = new ArrayList((Collection) iterable); + } + iterator = collection.iterator(); + } else { + iterator = iterable.iterator(); + } + } + + protected final void refresh(Iterator iterator) { + Objects.requireNonNull(iterator); + this.iterator = iterator; + } + + @Override + protected T supply() { + return iterator.next(); + } + + protected void remove() { + iterator.remove(); + } + + // One argument: The processed object + + public static IteratorTask create(Iterable iterable, Consumer processor) { + return create(iterable, false, processor); + } + + public static IteratorTask create(Iterable iterable, boolean clone, Consumer processor) { + return create(iterable, clone, object -> { + processor.accept(object); + return true; + }); + } + + public static IteratorTask create(Iterable iterable, Predicate processor) { + return create(iterable, false, processor); + } + + public static IteratorTask create(Iterable iterable, boolean clone, Predicate processor) { + return new IteratorTask(iterable, clone) { + @Override + protected boolean process(T object) { + return processor.test(object); + } + }; + } + + // Two arguments: the processed object, and a runnable to remove it from the iterator. + + public static IteratorTask create(Iterable iterable, BiConsumer processor) { + return create(iterable, false, processor); + } + + public static IteratorTask create(Iterable iterable, boolean clone, BiConsumer processor) { + return create(iterable, clone, (object, runnable) -> { + processor.accept(object, runnable); + return true; + }); + } + + public static IteratorTask create(Iterable iterable, BiPredicate processor) { + return create(iterable, false, processor); + } + + public static IteratorTask create(Iterable iterable, boolean clone, BiPredicate processor) { + return new IteratorTask(iterable, clone) { + @Override + protected boolean process(T object) { + return processor.test(object, this::remove); + } + }; + } + +} diff --git a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java index 1298c4c..6204bee 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java +++ b/dicore3/core/src/main/java/io/dico/dicore/task/VoidTask.java @@ -1,19 +1,19 @@ -package io.dico.dicore.task; - -import java.util.NoSuchElementException; - -public abstract class VoidTask extends BaseTask { - - @Override - protected final boolean process(Void object) { - return process(); - } - - @Override - protected final Void supply() throws NoSuchElementException { - return null; - } - - protected abstract boolean process(); - -} +package io.dico.dicore.task; + +import java.util.NoSuchElementException; + +public abstract class VoidTask extends BaseTask { + + @Override + protected final boolean process(Void object) { + return process(); + } + + @Override + protected final Void supply() throws NoSuchElementException { + return null; + } + + protected abstract boolean process(); + +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2c45a4..4f19e53 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d..518efff 100644 --- a/gradlew +++ b/gradlew @@ -1,172 +1,172 @@ -#!/usr/bin/env sh - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index f955316..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/javaStyle.xml b/javaStyle.xml index ec482f0..3a6a183 100644 --- a/javaStyle.xml +++ b/javaStyle.xml @@ -1,39 +1,39 @@ - -