Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
plugins {
id 'com.adarshr.test-logger' version '4.0.0'
}

allprojects {
group = 'fr.traqueur.commands'
version = property('version')
Expand All @@ -25,8 +21,6 @@ allprojects {
subprojects {
if (!project.name.contains('test-plugin')) {

apply plugin: 'com.adarshr.test-logger'

dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
testImplementation 'org.mockito:mockito-core:5.3.1'
Expand All @@ -40,7 +34,8 @@ subprojects {
}

testLogging {
events("passed", "skipped", "failed")
showStandardStreams = true
events("passed", "skipped", "failed", "standardOut", "standardError")
exceptionFormat "full"
}
}
Expand Down
6 changes: 6 additions & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'maven-publish'
id("me.champeau.jmh") version "0.7.3"
}

java {
Expand All @@ -9,6 +10,11 @@ java {
withJavadocJar()
}

dependencies {
jmh 'org.openjdk.jmh:jmh-core:1.37'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
}

def generatedResourcesDir = "$buildDir/generated-resources"

tasks.register('generateCommandsProperties') {
Expand Down
80 changes: 80 additions & 0 deletions core/src/jmh/java/fr/traqueur/commands/CommandLookupBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package fr.traqueur.commands;

import fr.traqueur.commands.api.models.Command;
import fr.traqueur.commands.api.models.collections.CommandTree;
import org.openjdk.jmh.annotations.*;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(2)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class CommandLookupBenchmark {

@Param({ "1000", "10000", "50000" })
public int N;

@Param({ "1", "2", "3" })
public int maxDepth;

private Map<String, DummyCommand> flatMap;
private CommandTree<DummyCommand, Object> tree;
private String[] rawLabels;

@Setup(Level.Trial)
public void setup() {
flatMap = new HashMap<>(N);
tree = new CommandTree<>();

rawLabels = new String[N];
ThreadLocalRandom rnd = ThreadLocalRandom.current();

for (int i = 0; i < N; i++) {
int depth = 1 + rnd.nextInt(maxDepth);
StringBuilder sb = new StringBuilder();
for (int d = 0; d < depth; d++) {
if (d > 0) sb.append('.');
sb.append("cmd").append(rnd.nextInt(N * 10));
}
String label = sb.toString();
rawLabels[i] = label;

DummyCommand cmd = new DummyCommand(label);
flatMap.put(label, cmd);
tree.addCommand(label, cmd);
}
}

@Benchmark
public DummyCommand mapLookup() {
String raw = rawLabels[ThreadLocalRandom.current().nextInt(N)];
String[] segments = raw.split("\\.");
for (int len = segments.length; len > 0; len--) {
String key = String.join(".", Arrays.copyOf(segments, len));
DummyCommand c = flatMap.get(key);
if (c != null) {
return c;
}
}
return null;
}

@Benchmark
public CommandTree.MatchResult<DummyCommand, Object> treeLookup() {
String raw = rawLabels[ThreadLocalRandom.current().nextInt(N)];
String[] parts = raw.split("\\.");
String base = parts[0];
String[] sub = Arrays.copyOfRange(parts, 1, parts.length);
return tree.findNode(base, sub).orElse(null);
}

public static class DummyCommand extends Command<DummyCommand, Object> {
public DummyCommand(String name) { super(null, name); }
@Override public void execute(Object s, fr.traqueur.commands.api.arguments.Arguments a) {}
}
}
188 changes: 0 additions & 188 deletions core/src/main/java/fr/traqueur/commands/api/CommandInvoker.java

This file was deleted.

27 changes: 17 additions & 10 deletions core/src/main/java/fr/traqueur/commands/api/CommandManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

import fr.traqueur.commands.api.arguments.Argument;
import fr.traqueur.commands.api.arguments.ArgumentConverter;
import fr.traqueur.commands.api.arguments.Arguments;
import fr.traqueur.commands.api.arguments.TabCompleter;
import fr.traqueur.commands.api.exceptions.ArgumentIncorrectException;
import fr.traqueur.commands.api.exceptions.TypeArgumentNotExistException;
import fr.traqueur.commands.api.logging.Logger;
import fr.traqueur.commands.api.logging.MessageHandler;
import fr.traqueur.commands.api.models.Command;
import fr.traqueur.commands.api.models.CommandInvoker;
import fr.traqueur.commands.api.models.CommandPlatform;
import fr.traqueur.commands.api.models.collections.CommandTree;
import fr.traqueur.commands.api.updater.Updater;
import fr.traqueur.commands.impl.arguments.BooleanArgument;
import fr.traqueur.commands.impl.arguments.DoubleArgument;
Expand Down Expand Up @@ -44,7 +49,7 @@ public abstract class CommandManager<T, S> {
/**
* The commands registered in the command manager.
*/
private final Map<String, Command<T,S>> commands;
private final CommandTree<T,S> commands;

/**
* The argument converters registered in the command manager.
Expand Down Expand Up @@ -86,7 +91,7 @@ public CommandManager(CommandPlatform<T,S> platform) {
this.messageHandler = new InternalMessageHandler();
this.logger = new InternalLogger(platform.getLogger());
this.debug = false;
this.commands = new HashMap<>();
this.commands = new CommandTree<>();
this.typeConverters = new HashMap<>();
this.completers = new HashMap<>();
this.invoker = new CommandInvoker<>(this);
Expand Down Expand Up @@ -165,10 +170,14 @@ public void unregisterCommand(String label) {
* @param subcommands If the subcommands must be unregistered.
*/
public void unregisterCommand(String label, boolean subcommands) {
if(this.commands.get(label) == null) {
throw new IllegalArgumentException("The command " + label + " does not exist.");
String[] rawArgs = label.split("\\.");
Optional<Command<T,S>> commandOptional = this.commands.findNode(rawArgs)
.flatMap(result -> result.node.getCommand());

if (!commandOptional.isPresent()) {
throw new IllegalArgumentException("Command with label '" + label + "' does not exist.");
}
this.unregisterCommand(this.commands.get(label), subcommands);
this.unregisterCommand(commandOptional.get(), subcommands);
}

/**
Expand Down Expand Up @@ -252,7 +261,7 @@ public Arguments parse(Command<T,S> command, String[] args) throws TypeArgumentN
* Get the commands of the command manager.
* @return The commands of the command manager.
*/
public Map<String, Command<T,S>> getCommands() {
public CommandTree<T, S> getCommands() {
return commands;
}

Expand Down Expand Up @@ -327,7 +336,7 @@ private void unregisterSubCommands(String parentLabel, List<Command<T,S>> subcom
*/
private void removeCommand(String label, boolean subcommand) {
this.platform.removeCommand(label, subcommand);
this.commands.remove(label);
this.commands.removeCommand(label, subcommand);
this.completers.remove(label);
}

Expand All @@ -351,10 +360,8 @@ private void addCommand(Command<T,S> command, String label) throws TypeArgumentN
}

command.setManager(this);

commands.put(label.toLowerCase(), command);

this.platform.addCommand(command, label);
commands.addCommand(label, command);

this.addCompletionsForLabel(labelParts);
this.addCompletionForArgs(label, labelSize, args);
Expand Down
Loading