Class ConfigBindings

java.lang.Object
com.ultikits.ultitools.manager.ConfigBindings

final class ConfigBindings extends Object
Resolution rules for a Scheduled period/delay or a CmdCD cooldown bound to a module config key (#531). One place for the shape rules, the key lookup and the value range, so the load-time check, the scheduler and the reload step cannot disagree about what a binding is.

A bound value is always a whole number of seconds, read from the @ConfigEntry field of the module's own registered config entity -- the same instance /ul reload reloads in place, resolved once at load and kept, so there is one source of truth. A reload that validateFields() refused never reaches the binding step; the one gap is a reload whose file write failed with an IOException, which ConfigManager.reloadConfigs logs and continues past without running the field's own validation annotations (#533), so only the binding's own range rule is guaranteed. The default lives only in that field's initializer; an annotation literal next to a binding is refused rather than used as a fallback, because after init() a declared key always has a value and a fallback would be a second, hand-synchronised copy of the default.

Ranges: a bound @Scheduled period or delay must be 1 to MAX_TICK_SECONDS seconds -- 0 does not mean "off" and does not mean "run once". A bound @CmdCD cooldown may be 0 to Integer.MAX_VALUE seconds, where 0 means "no cooldown" (maintainer ruling, 2026-09-23). Out of range is refused at load; on reload the running value is kept. A module that uses any binding must declare api-version: 630 or higher.

  • Field Details

    • TICKS_PER_SECOND

      static final long TICKS_PER_SECOND
      Bukkit's nominal tick rate. A bound seconds value is multiplied by this.
      See Also:
    • MAX_TICK_SECONDS

      static final long MAX_TICK_SECONDS
      Largest bound period or delay, in seconds: the tick count then fits in an int, so the reschedule arithmetic (int tick plus period, in long) cannot overflow. That is about 3.4 years (#531 gate-1 IN-03).
      See Also:
    • MAX_TICKS

      static final long MAX_TICKS
      The same ceiling in ticks: every interval of a bound task, bound or literal, is at most this.
      See Also:
    • MIN_API_VERSION

      static final int MIN_API_VERSION
      The lowest plugin.yml api-version a module using a binding may declare. An older framework silently drops the binding elements, so this floor is what makes it refuse the module instead (#531 gate-1 WR-03).
      See Also:
    • TIMER_RULE

      static final String TIMER_RULE
      The rule text for a bound period or delay.
      See Also:
    • COOLDOWN_RULE

      static final String COOLDOWN_RULE
      The rule text for a bound cooldown.
      See Also:
    • INTEGRAL_FIELD_TYPES

      private static final Set<Class<?>> INTEGRAL_FIELD_TYPES
      The field types a binding may read.
  • Constructor Details

    • ConfigBindings

      private ConfigBindings()
  • Method Details

    • isBound

      static boolean isBound(Scheduled scheduled)
      Parameters:
      scheduled - the annotation
      Returns:
      true if any of the three binding elements is set
    • isBound

      static boolean isBound(CmdCD cmdCD)
      Parameters:
      cmdCD - the annotation
      Returns:
      true if either binding element is set
    • checkShape

      static void checkShape(String owner, Scheduled scheduled)
      Refuses a Scheduled binding whose elements contradict each other: a key without a config class, a config class without any key, async = true (a bound task must be sync, so that its runs are observed on the main thread where a reload reschedules it -- #531 gate-1 round 2), or a literal set alongside the key that replaces it.
      Parameters:
      owner - Class.method, for the message
      scheduled - the bound annotation
      Throws:
      PluginModuleException - naming the owner and the contradicting elements
    • checkNamesABinding

      private static void checkNamesABinding(String owner, Scheduled scheduled)
      A key without a config class, a config class without a key, or async = true.
    • checkNoLiteralBesideItsKey

      private static void checkNoLiteralBesideItsKey(String owner, Scheduled scheduled)
      A literal set alongside the key that replaces it: the default lives only in the config field.
    • checkLiteralHalfWithinCeiling

      private static void checkLiteralHalfWithinCeiling(String owner, Scheduled scheduled)
      The literal half of a partially bound task is held to the same tick ceiling as the bound half, so every interval of a bound task fits in Bukkit's int tick window and the reschedule arithmetic stays exact across its wraparound (Codex round 10 on #536).
    • checkShape

      static void checkShape(String owner, CmdCD cmdCD)
      Refuses a CmdCD binding whose elements contradict each other.
      Parameters:
      owner - the executor class, or Class.method, for the message
      cmdCD - the bound annotation
      Throws:
      PluginModuleException - naming the owner and the contradicting elements
    • resolve

      static ConfigBindings.Source resolve(UltiToolsPlugin plugin, String owner, Class<? extends AbstractConfigEntity> configClass, String key)
      Resolves key on the single config entity of configClass registered for plugin.
      Parameters:
      plugin - the module
      owner - Class.method or the executor class, for the message
      configClass - the bound config class
      key - the @ConfigEntry path
      Returns:
      the resolved source
      Throws:
      PluginModuleException - when the class is not registered exactly once, the key names no @ConfigEntry field, or the field is not integral
      ConfigurationException - when the entity's load failed with an IOException that ConfigManager caught, so its values were never validated
    • findEntryField

      private static Field findEntryField(Class<?> entityClass, String key)
      Returns:
      the @ConfigEntry field of entityClass (declared or inherited) whose path -- or field name, when the path is empty -- equals key, or null
    • isValidTimerSeconds

      static boolean isValidTimerSeconds(Long seconds)
      Parameters:
      seconds - a bound value
      Returns:
      true for a scheduling value in [1, MAX_TICK_SECONDS]
    • isValidCooldownSeconds

      static boolean isValidCooldownSeconds(Long seconds)
      Parameters:
      seconds - a bound value
      Returns:
      true for a cooldown value in [0, Integer.MAX_VALUE]; 0 means no cooldown
    • checkApiVersionFloor

      static void checkApiVersionFloor(UltiToolsPlugin plugin, String firstBinding)
      Refuses a module that uses a binding while declaring an api-version below MIN_API_VERSION.
      Parameters:
      plugin - the module
      firstBinding - the first bound declaration found, for the message
      Throws:
      PluginModuleException - if the declared floor is too low
    • invalidValueAtLoad

      static ConfigurationException invalidValueAtLoad(UltiToolsPlugin plugin, String owner, ConfigBindings.Source source, Long value, String rule)
      The load-time refusal for an out-of-range bound value -- the same exception type and the same "file was not modified" contract as a @Range violation.
      Parameters:
      plugin - the module refusing to load
      owner - Class.method or the executor class
      source - the binding
      value - the refused value
      rule - the rule it broke
      Returns:
      the exception to throw
    • shapeError

      private static PluginModuleException shapeError(String owner, String detail)