@startuml command-class-diagram title CR-Core — Command framework (class diagram, nested sub-commands) skinparam classAttributeIconSize 0 hide empty members package "fr.luc.crcore.command" { interface Command { + getName(): String + getAliases(): List + getPermission(): String + isPlayerOnly(): boolean + getDescription(): String + execute(ctx: CommandContext): CommandResult + tabComplete(sender, args: String[]): List + matches(label: String): boolean } abstract class AbstractCommand { - name: String - aliases: List - permission: String - playerOnly: boolean - description: String - usage: String - arguments: List - subCommandsByName: Map - subCommandsByAlias: Map -- # addAlias(...) / permission / playerOnly / description / usage # argument(name, type) / optionalArgument(name, type) # addSubCommand(sub: SubCommand): void -- + findSubCommand(label): Optional + getSubCommands(): Collection + replaceSubCommand(name, newSub): Optional + hasSubCommands(): boolean -- + dispatch(sender, label, args): CommandResult + tabComplete(sender, args): List + execute(ctx): CommandResult # listSubCommands(ctx): CommandResult # checkAccess(sender): boolean # buildContext(sender, label, rawArgs): CommandContext } abstract class BaseCommand { + onCommand(sender, cmd, label, args): boolean + onTabComplete(sender, cmd, alias, args): List # handleResult(sender, result): void } abstract class SubCommand class CommandContext { - sender: CommandSender - label: String - rawArgs: String[] - parsedArgs: Map + getSender / isPlayer / getPlayer / requirePlayer + get(name): T + getOptional(name): Optional + has(name): boolean + reply(msg): void } class CommandResult { - type: Type - message: String + {static} success / failure / invalidUsage / noPermission / playerOnly } enum "CommandResult.Type" as ResultType { SUCCESS FAILURE INVALID_USAGE NO_PERMISSION PLAYER_ONLY } class CommandException interface "ArgumentType" as ArgumentType { + parse(input: String): T + suggestions(sender, partial): List } class ArgumentTypes <> { + STRING / INTEGER / DOUBLE / BOOLEAN / ONLINE_PLAYER + enumOf(Class): ArgumentType + choice(String...): ArgumentType } class ArgumentDef <> { - name: String - type: ArgumentType - required: boolean } AbstractCommand ..|> Command BaseCommand --|> AbstractCommand SubCommand --|> AbstractCommand BaseCommand ..|> "org.bukkit.command.CommandExecutor" BaseCommand ..|> "org.bukkit.command.TabCompleter" AbstractCommand "1" o-- "*" SubCommand : sub-commands\n(recursive) AbstractCommand "1" *-- "*" ArgumentDef : arguments ArgumentDef --> ArgumentType CommandResult +-- ResultType CommandException --|> RuntimeException AbstractCommand ..> CommandContext : creates AbstractCommand ..> CommandResult : returns } note bottom of AbstractCommand Le routage est récursif : /core team create → CoreCommand.dispatch("team", ["create",...]) → TeamGroup.dispatch("create", [...]) → TeamCreate.execute(ctx) end note @enduml