# Class Binders

java.lang.Object
com.codename1.binding.Binders

public final class Binders extends Object

Public entry point for the build-time component binding framework.

`@Bindable` classes get a generated binder at build time. Each binder's static initializer self-registers with this registry. The registry stays empty until something triggers each generated class's `<clinit>`:

- iOS / Android -- the build server probes the project zip for `cn1app.BinderBootstrap` and splices a `new cn1app.BinderBootstrap();` into the per-build application stub before `Display.init`.
- JavaSE simulator + desktop -- `JavaSEPort#postInit` calls `Class.forName("cn1app.BinderBootstrap")` so the registry is populated on the same boundary.
- Unit tests / manual init -- application code can call `Binders.register(...)` directly.

### Two-way bindings and the change-notification contract

A binding declared `twoWay = true` flows in both directions:

1. Component -> model. The generated binder installs a listener on the editable component (`TextField`, `CheckBox`, etc.). When the user mutates the component, the binder calls the model's setter (or writes the public field directly), and the setter's instrumented exit calls `notifyChanged(this)`.
2. Model -> component. Application code that mutates the model through a setter (instrumented by the build-time processor) causes `notifyChanged(this)` to fire, which walks every live binding for that model and pushes the new value into the matching component.

The two paths together would loop forever if left to themselves -- the model setter fires a change event, which refreshes the component, which fires its own change event, which calls the model setter again. To break the loop, every framework-initiated mutation runs inside an "update region" guarded by a thread-local flag. While the flag is set, `notifyChanged` is a no-op, and component listeners short-circuit.

Concretely:

- `Binders.bind(model, container)` enters an update region for the initial model -> component push.
- `Binding#refresh()` enters an update region for every subsequent model -> component push.
- `Binding#commit()` enters an update region for the component -> model pull.
- The generated component listener enters an update region before calling the setter.

Limitation: when a setter synchronously mutates a second bound field (e.g. `setFirstName` also calls `setFullName`), the second field's notification is also suppressed -- the user must call `binding.refresh()` explicitly if they want the second component to catch up. See `Annotation-Component-Binding.asciidoc` for details.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static <T> Binding`
  `bind(T model, Container container)`
  Pushes the model values into the matching components in `container`, wiring up the two-way listeners declared on its `@Bind` fields.
  `static void`
  `enterUpdate()`
  Enter an update region.
  `static void`
  `exitUpdate()`
  Exit an update region.
  `static <T> Binder<T>`
  `get(Class<T> type)`
  Looks up the binder for `type` (by `type.getName()`) or null when none is registered.
  `static boolean`
  `isInUpdate()`
  True while the calling thread is inside a binding update region.
  `static void`
  `notifyChanged(Object model)`
  Called from the build-time-instrumented setter at every return point.
  `static <T> void`
  `register(Binder<T> binder)`
  Installs `binder` under `binder.type().getName()`.
  `static void`
  `registerBinding(NotifiableBinding binding)`
  Generated binders call this to enroll a new live binding in the per-class registry.
  `static void`
  `unregisterBinding(NotifiableBinding binding)`
  Inverse of `registerBinding`.

  ### Methods inherited from class Object

  `clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### register

    public static <T> void register(Binder<T> binder)
    Installs `binder` under `binder.type().getName()`. The generated per-class binder's static initializer calls this; hand-written binders for classes outside the build's annotation scan call it explicitly.

  - ### get

    public static <T> Binder<T> get(Class<T> type)
    Looks up the binder for `type` (by `type.getName()`) or null when none is registered.

  - ### bind

    public static <T> Binding bind(T model, Container container)
    Pushes the model values into the matching components in `container`, wiring up the two-way listeners declared on its `@Bind` fields. Throws `IllegalStateException` when no binder is registered for `model.getClass()`.

  - ### notifyChanged

    public static void notifyChanged(Object model)

    Called from the build-time-instrumented setter at every return point. Walks the live bindings for `model.getClass().getName()` and refreshes those whose source is `model`. Short-circuits when the calling thread is already inside an update region, breaking the model -> component -> model loop.

    Application code rarely calls this directly; the build-time instrumentation wires it into generated setters automatically.

  - ### registerBinding

    public static void registerBinding(NotifiableBinding binding)
    Generated binders call this to enroll a new live binding in the per-class registry. Call `unregisterBinding` from `disconnect` to remove it.

  - ### unregisterBinding

    public static void unregisterBinding(NotifiableBinding binding)
    Inverse of `registerBinding`. Called from `Binding#disconnect`.

  - ### enterUpdate

    public static void enterUpdate()
    Enter an update region. Generated binder code calls this around every framework-initiated mutation -- model->component pushes and component->model pulls -- so the setter notification and the component change listener both short-circuit while we're inside.

  - ### exitUpdate

    public static void exitUpdate()
    Exit an update region. Call once per `enterUpdate`.

  - ### isInUpdate

    public static boolean isInUpdate()
    True while the calling thread is inside a binding update region. Generated component listeners check this and bail out early.