Class TabCompletionManager

java.lang.Object
com.ultikits.ultitools.commands.tabcomplete.TabCompletionManager

public class TabCompletionManager extends Object
Central manager for tab completion functionality. Provides a unified API for both old and new command systems.
Since:
6.2.0
Version:
1.0.0
Author:
wisdomme
  • Field Details

    • PLAYERS

      public static final String PLAYERS
      Built-in completer prefix for players.
      See Also:
    • WORLDS

      public static final String WORLDS
      Built-in completer prefix for worlds.
      See Also:
    • MATERIALS

      public static final String MATERIALS
      Built-in completer prefix for materials.
      See Also:
    • BLOCKS

      public static final String BLOCKS
      Built-in completer prefix for blocks.
      See Also:
    • ITEMS

      public static final String ITEMS
      Built-in completer prefix for items.
      See Also:
    • BOOLEAN

      public static final String BOOLEAN
      Built-in completer prefix for boolean values.
      See Also:
    • TOGGLE

      public static final String TOGGLE
      Built-in completer prefix for toggle values.
      See Also:
    • LOGGER

      private static final Logger LOGGER
    • instance

      private static volatile TabCompletionManager instance
    • completers

      private final Map<String,TabCompletionManager.Registration> completers
      Key -> its registration: the completer together with the owner name and owner instance the registration was attributed to (05-06 / D-08, T-05-24; #506). One immutable entry per key, replaced as a whole by register(String, TabCompleter) and removed by the owner sweeps only while it is still the exact entry they matched, so a key registered again meanwhile -- from another thread, or by anything the sweep triggers -- keeps its new completer (Codex review of #564, round 4). The owner fields are recorded only while a beginRegistrationScope(String)/endRegistrationScope() window is active; a completer registered outside one is "unowned" and never swept. Keyed by the completer KEY (a String), not by the completer's Class -- Phase 1 D-35/D-38 forbids static Class-keyed maps because they pin a plugin's ClassLoader. An owner instance is held only until that module is unloaded, exactly as the completer object itself is.
    • methodCompleter

      private final MethodInvocationCompleter methodCompleter
    • currentOwner

      private final ThreadLocal<String> currentOwner
      The owner currently attributed to registrations made via register(String, TabCompleter) -- set by beginRegistrationScope(String), cleared by endRegistrationScope(). ThreadLocal rather than a plain field because getInstance() is one process-wide singleton and PluginManager's own module loading, while sequential today, should not silently corrupt ownership if that ever changes.
    • currentOwnerInstance

      private final ThreadLocal<UltiToolsPlugin> currentOwnerInstance
      The module instance attributed to registrations in the current scope, if the scope names one (#506). Two copies of one module share a name, so only the instance tells a superseded copy's completers from its replacement's; unregisterByOwnerInstance(UltiToolsPlugin) releases by it.
  • Constructor Details

    • TabCompletionManager

      private TabCompletionManager()
      Private constructor - use getInstance().
  • Method Details

    • getInstance

      public static TabCompletionManager getInstance()
      Gets the singleton instance.
      Returns:
      the tab completion manager instance
    • registerBuiltInCompleters

      private void registerBuiltInCompleters()
      Registers all built-in completers.
    • register

      public void register(String key, TabCompleter completer)
      Registers a custom completer with a key.
      Parameters:
      key - the key to register with (e.g., "@custom")
      completer - the completer to register
    • unregister

      public void unregister(String key)
      Unregisters a completer.
      Parameters:
      key - the key to unregister
    • beginRegistrationScope

      public void beginRegistrationScope(String owner)
      Marks the beginning of a registration scope attributed to owner -- every completer key registered via register(String, TabCompleter) while this scope is active is recorded as owned by owner, so unregisterByOwner(String) can sweep it later without requiring caller code to pass an owner explicitly (05-06 / D-08's "explicit owner parameter on an internal registration path" -- the parameter is ambient rather than a new argument on the published register(String, TabCompleter) signature, which stays frozen).

      ClassLoader-derived ownership was considered and rejected for this codebase. Every internal UltiToolsPlugin module is loaded through ONE shared URLClassLoader covering the whole plugins/ folder (see PluginManager.init(ClassLoader)) -- the identical structural finding already recorded at PluginManager.validateAdditionalEntity's javadoc for D-19 -- so a completer instance's getClass().getClassLoader() cannot distinguish between two different modules here. An explicit scope is the only mechanism reachable from a module's only registration entry point (the public register(String, TabCompleter)) that still correctly separates two modules' completers.

      Not part of the frozen 6.2.5 singleton shape (getInstance(), register(String, TabCompleter), unregister(String)) -- new in 6.3.0, intended for PluginManager's own module load/unload sequence, not for module authors.

      Scopes do not nest: a second beginRegistrationScope call while one is already active simply replaces the current owner. UltiTools' own module loading is sequential -- one module's container is fully assembled (including its pluginContext.refresh(), where @PostConstruct runs) before the next module's load begins -- so nesting has never been required; this is a stated assumption, not an enforced invariant.

      Parameters:
      owner - the identifier (the framework uses the plugin's name) to attribute subsequent registrations to
      Since:
      6.3.0
    • beginRegistrationScope

      public void beginRegistrationScope(String owner, UltiToolsPlugin ownerInstance)
      Same as beginRegistrationScope(String), and additionally attributes every key registered in the scope to ownerInstance, so unregisterByOwnerInstance(UltiToolsPlugin) can release exactly that module instance's completers (#506). PluginManager opens this form for every module it loads: when a newer copy of a module replaces an older one, both copies share a name, and only the instance tells their completers apart.
      Parameters:
      owner - the name to attribute subsequent registrations to, as in beginRegistrationScope(String)
      ownerInstance - the module instance to attribute them to; null records none, which is exactly beginRegistrationScope(String)
      Since:
      6.3.0
    • endRegistrationScope

      public void endRegistrationScope()
      Ends the current registration scope started by beginRegistrationScope(String). Registrations made after this call (and before another scope begins) are unowned and never swept by unregisterByOwner(String) -- the same as every registration made before this mechanism existed.
      Since:
      6.3.0
    • unregisterByOwner

      public int unregisterByOwner(String owner)
      Bulk-unregisters every completer key currently attributed to owner by an earlier beginRegistrationScope(String)/endRegistrationScope() window. Each key is removed only while it still holds the registration the sweep matched, so a key registered again meanwhile keeps its new completer (Codex review of #564, round 4). A key registered outside any scope (core built-ins, or any caller that never calls beginRegistrationScope(String)) has no recorded owner and is never matched here, regardless of owner's value.
      Parameters:
      owner - the owner identifier to sweep; null matches nothing and is a no-op
      Returns:
      the number of keys unregistered
      Since:
      6.3.0
    • unregisterByOwnerInstance

      public int unregisterByOwnerInstance(UltiToolsPlugin ownerInstance)
      Unregisters every completer key registered in a scope that named ownerInstance (#506), whatever name the scope carried. A key another module or another copy of the same module registered afterwards belongs to that registrant and is not touched, and a key registered without an instance is never matched here -- unregisterByOwner(String) still releases those by name.
      Parameters:
      ownerInstance - the module instance being unloaded; null matches nothing
      Returns:
      the number of keys unregistered
      Since:
      6.3.0
    • releaseForSupersede

      @Internal public Runnable releaseForSupersede(UltiToolsPlugin ownerInstance)
      Releases every completer key registered in a scope that named ownerInstance and returns the action that gives them back (#562). PluginManager calls this for a loaded copy of a module just before a newer copy of the same module builds its container and runs registerSelf(), so the newer copy registers its keys into a registry that holds none of the older copy's. When the newer copy fails to load, the framework first releases what the failed copy registered and then runs the returned action, which puts each released completer back under its key, unchanged; when the newer copy loads, the action is dropped and the older copy is unloaded. Running the action more than once restores nothing more.

      As in the owner sweeps, a key is removed only while it still holds the registration this call matched, and put back only while it is free: a key registered again in between keeps its new completer, and one WARNING names the key -- a copy never takes over another copy's key. Intended for PluginManager, not for module authors.

      Parameters:
      ownerInstance - the loaded copy being superseded; null releases nothing
      Returns:
      the action that restores what this call released
      Since:
      6.3.0
    • restore

      private void restore(Map<String,TabCompletionManager.Registration> released)
    • getCompleter

      public TabCompleter getCompleter(String key)
      Gets a registered completer by key.
      Parameters:
      key - the completer key
      Returns:
      the completer or null if not found
    • completerFor

      private TabCompleter completerFor(String key)
    • suggest

      public List<String> suggest(org.bukkit.entity.Player player, org.bukkit.command.Command command, String[] args)
      Generates suggestions using the appropriate completer.
      Parameters:
      player - the player requesting completion
      command - the command
      args - the current arguments
      Returns:
      list of suggestions
    • suggest

      public List<String> suggest(TabCompletionContext context)
      Generates suggestions using the given context.
      Parameters:
      context - the completion context
      Returns:
      list of suggestions
    • suggest

      public List<String> suggest(TabCompletionContext context, String resolvedSuggest)
      Resolves suggestions using an explicitly-resolved @CmdParam.suggest() value, rather than context's TabCompletionContext.parameterName -- which carries @CmdParam.value(), the parameter's DISPLAY NAME, not its suggestion (05-06 / D-07 Pitfall 2, T-05-28). suggest(TabCompletionContext) above is left untouched for existing callers; this overload is the dual-notation entry point a caller that has already resolved the real suggest() value (see resolveSuggestValue(Method, String)) should use instead.

      A resolvedSuggest beginning with @ resolves as a built-in or registered completer key. Any other value -- including null or empty -- falls through to the existing method-invocation completer (and its i18n hint-text fallback), unchanged.

      Parameters:
      context - the completion context
      resolvedSuggest - the already-resolved @CmdParam.suggest() string for the parameter being completed; may be null or empty
      Returns:
      the suggestions; never null
      Since:
      6.3.0
    • resolveSuggestValue

      public static String resolveSuggestValue(Method matchedMethod, String parameterName)
      Resolves matchedMethod's @CmdParam.suggest() value for the parameter whose @CmdParam.value() equals parameterName -- i.e. it keys the lookup off the parameter's DISPLAY NAME to find the right parameter, then returns that parameter's suggestion, never the display name itself (05-06 / D-07). This mirrors MethodInvocationCompleter.getSuggestName's existing lookup shape so the two stay consistent, without depending on that private method directly.
      Parameters:
      matchedMethod - the matched @CmdMapping method, or null
      parameterName - the parameter's display name (@CmdParam.value()) to look up
      Returns:
      the resolved suggest() value, or null if matchedMethod is null or no parameter's value() matches parameterName
      Since:
      6.3.0
    • suggestWith

      public List<String> suggestWith(TabCompletionContext context, String completerKey)
      Suggests completions for a specific parameter type.
      Parameters:
      context - the completion context
      completerKey - the key of the completer to use
      Returns:
      list of suggestions
    • createContext

      public TabCompletionContext createContext(org.bukkit.entity.Player player, org.bukkit.command.Command command, String[] args)
      Creates a context for tab completion with common defaults.
      Parameters:
      player - the player
      command - the command
      args - the arguments
      Returns:
      a tab completion context
    • filterByInput

      public static List<String> filterByInput(List<String> suggestions, String input)
      Helper method to filter suggestions by current input.
      Parameters:
      suggestions - the suggestions to filter
      input - the current input
      Returns:
      filtered suggestions
    • suggestFirstArgs

      public List<String> suggestFirstArgs(Map<String,Method> mappings, TabCompletionContext context)
      Suggests command format first arguments from mappings.
      Parameters:
      mappings - the command mappings (format -> method)
      context - the completion context
      Returns:
      list of first argument suggestions