Class SHelper

java.lang.Object
com.vaadin.swingbridge.surrogates.SHelper

public final class SHelper extends Object
Shared plumbing for com.vaadin.swingbridge.surrogates.*. Surrogate-local mirror of vaadinx.EHelper — module direction is :emulators → :surrogates, so we can't reuse the emulator's helper without inverting it (SD_shelper_statics).
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    Test-only capture sink for onUnimplemented(Object, String, Object...).
  • Method Summary

    Modifier and Type
    Method
    Description
    static <T extends EventListener>
    T[]
    The stored listeners of type in AWT dispatch order — first-registered-first, which is the reverse of what EventListenerList yields.
    static void
    callSwing(Runnable runnable)
    Uniform funnel for Vaadin peer → Swing-side callbacks.
    static void
    markContentPaneSpan(com.vaadin.flow.component.Component host, com.vaadin.flow.component.Component pane, com.vaadin.flow.component.Component old)
    Tags a window's content pane, and the window itself, so the content pane fills the window body instead of rendering at intrinsic height inside it.
    static void
    markLayeredPane(com.vaadin.flow.component.Component layeredPane)
    Tags a layered pane display: contents, so its children become direct flex items of the root pane's column and the pane itself contributes no geometry.
    static void
    markMenuBarSlot(com.vaadin.flow.component.Component bar, com.vaadin.flow.component.Component old)
    Tags a menu bar to take its intrinsic height in the root pane's column, leaving the rest to the content pane — the CSS spelling of JRootPane.RootLayout's menuBar.setBounds(0, 0, w, mbd.height).
    static void
    markRootPane(com.vaadin.flow.component.Component rootPane)
    Tags a root pane so it becomes the flex column the menu bar and content pane stack inside, and the positioned ancestor the glass pane insets against.
    static void
    offerSession(com.vaadin.flow.component.Component peer, com.vaadin.flow.server.VaadinSession session)
    Records session as peer's unless it already has one: how an emulator built off the UI thread, whose context knows the session, hands it to a surrogate whose constructor found none current.
    static void
    onNoop(Object self, String method)
    Deliberate no-op hook for Swing API that's redundant in the Vaadin world (layout invalidation, repaint, peer lifecycle, paint dispatch).
    static void
    onUnimplemented(Object self, String method, Object... args)
    Warn-and-continue stub hook for unimplemented surrogate API.
    static void
    onUnsupportedPeerShape(String className, String situation)
    ERROR-log hook for situations where emulation can't honor a well-formed Swing request because the peer-element shape is wrong — e.g. setting a LayoutManager on a Container whose host element isn't a plain <div>.
    static String
    Resolve a class to a non-empty Class.getSimpleName() by walking up the superclass chain past anonymous-class frames (whose simpleName is "").
    static void
    runOnOwnerUI(com.vaadin.flow.component.Component owner, Runnable body)
    Runs body on owner's UI thread and returns once it has run — the surrogate-side hop for the carve-out in R_tolerate_off_ui_thread's surrogate limb, which allows it only inside a callback from a Swing model.
    static void
    runOnSession(com.vaadin.flow.server.VaadinSession session, com.vaadin.flow.component.Component peer, Runnable body)
    Runs body, which touches peer, under session's lock and with peer's UI current, and returns once it has run: the one implementation of the UI hop, behind both runOnOwnerUI(Component, Runnable) and the emulators' withPeer.
    static com.vaadin.flow.server.VaadinSession
    sessionOf(com.vaadin.flow.component.Component peer)
    The session peer captured — at construction when one was current, else handed down by its emulator (offerSession(Component, VaadinSession)), else at first attach — or null if it has none.
    static void
    stampSwingClass(com.vaadin.flow.component.Component target, Class<?> klass)
    Stamp the data-swing-class attribute on a peer / surrogate element so DevTools shows which Swing class a Vaadin element is standing in for.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • warnHook

      public static Consumer<String> warnHook
      Test-only capture sink for onUnimplemented(Object, String, Object...). Tests install a collector to assert "this path fires no stub-level WARNs" without having to reconfigure logging; production stays on the no-op default so call sites can feed the hook unconditionally.
  • Method Details

    • onUnimplemented

      public static void onUnimplemented(Object self, String method, Object... args)
      Warn-and-continue stub hook for unimplemented surrogate API. Logs WARN and returns; never throws (R_match_swing_errors / SD_dropin_stance). The first arg is the receiver instance — its runtime class shows up in the log, so a surrogate extending different Vaadin types each report their actual class name.
    • callSwing

      public static void callSwing(Runnable runnable)
      Uniform funnel for Vaadin peer → Swing-side callbacks. Every surrogate's Vaadin-listener body whose job is to fire a Swing-style event (model change, ChangeListener fan-out, focus, value-change, window closing, …) wraps through here instead of calling runnable.run() directly.

      Today this is literally runnable.run() and will stay that way (SD_sframe). Blocking-dialog handling is an :emulators-layer concern: Dialog.setOpened(boolean) and modal JOptionPane.showXxxDialog calls don't block the Vaadin request thread without the UI-fiber hook, and that hook lives only in vaadinx.EHelper.callSwing — surrogates can't hack around it without duplicating machinery we intentionally scope to the emulator layer. The seam survives here as a uniform peer→Swing funnel for future cross-cutting work (log instrumentation, error-handler routing, etc.) that would otherwise have to audit every listener.

      Exceptions propagate. Vaadin's ErrorHandler is the proper escalation path; swallowing here would pre-empt user error handling and hide real bugs.

    • runOnOwnerUI

      public static void runOnOwnerUI(com.vaadin.flow.component.Component owner, Runnable body)
      Runs body on owner's UI thread and returns once it has run — the surrogate-side hop for the carve-out in R_tolerate_off_ui_thread's surrogate limb, which allows it only inside a callback from a Swing model.
      // SJComboBox's ListDataListener, on whatever thread mutated the model:
      // Allowed by R_tolerate_off_ui_thread because callback from model: ComboBoxModel ListDataListener
      SHelper.runOnOwnerUI(this, () -> onModelEvent(e));
      

      Goes through the session owner captured (sessionOf(Component)); see runOnSession(VaadinSession, Component, Runnable) for what happens under its lock.

      Synchronous by design, per SD_background_model_hop: Swing fires a model's listeners before the mutator returns, and a deferred body would reorder them against the caller's next line.

    • runOnSession

      public static void runOnSession(com.vaadin.flow.server.VaadinSession session, com.vaadin.flow.component.Component peer, Runnable body)
      Runs body, which touches peer, under session's lock and with peer's UI current, and returns once it has run: the one implementation of the UI hop, behind both runOnOwnerUI(Component, Runnable) and the emulators' withPeer.

      Nothing about peer is read before the lock is held. Its state node, its parent chain and getUI() are written by the UI thread under that lock, so an unlocked read races it, and Composite.getElement() even initialises content. Under the lock, the UI owning the peer's state tree is the one made current; a peer detached after being attached still names its old tree, which Flow still guards.

      Inline when a UI is already current (the caller holds the lock), and when session is null: the peer has captured none, so it has never been attached, has no state tree, and has no lock to take. If the hop itself fails before the body starts, it is logged at WARN and the body runs inline, unlocked. An exception from the body always propagates, and the body never runs twice.

      Parameters:
      session - the session peer captured, or null if it has none
    • sessionOf

      public static com.vaadin.flow.server.VaadinSession sessionOf(com.vaadin.flow.component.Component peer)
      The session peer captured — at construction when one was current, else handed down by its emulator (offerSession(Component, VaadinSession)), else at first attach — or null if it has none. Reads the surrogate's private hopSession field, which is volatile; a component with no such field (a bare Vaadin component) answers null.
    • offerSession

      public static void offerSession(com.vaadin.flow.component.Component peer, com.vaadin.flow.server.VaadinSession session)
      Records session as peer's unless it already has one: how an emulator built off the UI thread, whose context knows the session, hands it to a surrogate whose constructor found none current. No-op for a bare Vaadin component or a null session.
    • onNoop

      public static void onNoop(Object self, String method)
      Deliberate no-op hook for Swing API that's redundant in the Vaadin world (layout invalidation, repaint, peer lifecycle, paint dispatch). Logs at DEBUG only — these get hit hot, and unlike onUnimplemented(Object, String, Object...) the call is answered by design, not deferred work. Does not feed warnHook: the testapps WARN-inventory gates assert emptiness, and a deliberate-noop should not count as a stub regression.

      No varargs on purpose: args add no information for a call we ignored by design, and omitting them keeps the hot path allocation-free.

    • onUnsupportedPeerShape

      public static void onUnsupportedPeerShape(String className, String situation)
      ERROR-log hook for situations where emulation can't honor a well-formed Swing request because the peer-element shape is wrong — e.g. setting a LayoutManager on a Container whose host element isn't a plain <div>. Mirrors vaadinx.EHelper.onUnsupportedPeerShape verbatim — className is used directly, not resolved from an object's runtime class: callers here pass a descriptive name (e.g. the offending LayoutManager's name) rather than the offending instance. Louder than onUnimplemented(Object, String, Object...)'s WARN because the user has stepped into genuinely broken territory; returns without throwing per R_match_swing_errors.
    • resolveSwingClassName

      public static String resolveSwingClassName(Class<?> klass)
      Resolve a class to a non-empty Class.getSimpleName() by walking up the superclass chain past anonymous-class frames (whose simpleName is ""). User code that does new SJButton() { ... } thus stamps as "SJButton", not blank.
    • stampSwingClass

      public static void stampSwingClass(com.vaadin.flow.component.Component target, Class<?> klass)
      Stamp the data-swing-class attribute on a peer / surrogate element so DevTools shows which Swing class a Vaadin element is standing in for. Last-write-wins: when an emulator wraps a surrogate, the emulator's stamp overwrites the surrogate's — stage-2 user code sees the emulator class, stage-3 user code sees the surrogate class.
    • markContentPaneSpan

      public static void markContentPaneSpan(com.vaadin.flow.component.Component host, com.vaadin.flow.component.Component pane, com.vaadin.flow.component.Component old)
      Tags a window's content pane, and the window itself, so the content pane fills the window body instead of rendering at intrinsic height inside it.

      Both classes are defined in emul/swindow.css, which carries the measurements and the reason flex-basis must stay auto. Call after the pane is planted, and on every swap — passing the pane being replaced as old so it gives the class back.

      Parameters:
      old - the content pane being replaced, or null on first plant
    • markRootPane

      public static void markRootPane(com.vaadin.flow.component.Component rootPane)
      Tags a root pane so it becomes the flex column the menu bar and content pane stack inside, and the positioned ancestor the glass pane insets against. The rule is in emul/swindow.css; call once per root pane, at construction.

      Both layers call this on the same element — the emulator's JRootPane peers on the surrogate it tags — so the class is idempotent by construction.

    • markLayeredPane

      public static void markLayeredPane(com.vaadin.flow.component.Component layeredPane)
      Tags a layered pane display: contents, so its children become direct flex items of the root pane's column and the pane itself contributes no geometry. Measured a layout no-op; see emul/swindow.css for what breaks with a bare Div instead.

      Applies to the instance a root pane owns, never to JLayeredPane as a class — JDesktopPane extends it, and a universal rule would strip every user-instantiated one of its containing block.

    • markMenuBarSlot

      public static void markMenuBarSlot(com.vaadin.flow.component.Component bar, com.vaadin.flow.component.Component old)
      Tags a menu bar to take its intrinsic height in the root pane's column, leaving the rest to the content pane — the CSS spelling of JRootPane.RootLayout's menuBar.setBounds(0, 0, w, mbd.height).
      Parameters:
      old - the menu bar being replaced, or null on first plant
    • awtOrder

      public static <T extends EventListener> T[] awtOrder(EventListenerList list, Class<T> type)
      The stored listeners of type in AWT dispatch order — first-registered-first, which is the reverse of what EventListenerList yields.
      Type Parameters:
      T - an AWT listener type. Swing listener types must not come through here: AWTEventMulticaster dispatches first-registered-first while Swing's list is last-first, and each convention is correct for its own half of the JDK (D_awt_dead_hooks).
      Returns:
      a fresh array, reversed in place; iterate it rather than re-reading the list, so in-dispatch listener mutation stays safe