Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class ComponentEvent<T extends Component> extends EventObject {

private boolean fromClient = false;
private Command unregisterListenerCommand = null;
private UI ui;

/**
* Creates a new event using the given source and indicator whether the
Expand All @@ -50,36 +51,78 @@ public ComponentEvent(T source, boolean fromClient) {
this.fromClient = fromClient;
}

/**
* Creates a new event using the given source, indicator whether the event
* originated from the client side or the server side, and an explicit UI to
* associate with the event.
* <p>
* Use this constructor when the source component may not be attached to a
* UI at the time the event is fired, but the UI is still known (for
* example, when the event is dispatched from code that has access to the
* current UI). The supplied UI is then returned by {@link #getUI()} without
* relying on the source's attachment state.
*
* @param source
* the source component
* @param fromClient
* <code>true</code> if the event originated from the client
* side, <code>false</code> otherwise
* @param ui
* the UI associated with the event, or <code>null</code> if not
* available
*/
public ComponentEvent(T source, boolean fromClient, UI ui) {
super(source);
this.fromClient = fromClient;
this.ui = ui;
}

@SuppressWarnings("unchecked")
@Override
public T getSource() {
return (T) super.getSource();
}

/**
* Gets the UI the source component is attached to.
* Gets the UI associated with this event.
* <p>
* This is a convenience for {@code getSource().getUI().get()} when the
* event is fired while the source is attached to a UI, which is the common
* case.
* The UI is resolved in the following order:
* <ol>
* <li>If a {@link UI} was explicitly provided at
* {@linkplain #ComponentEvent(Component, boolean, UI) construction time},
* that instance is returned.</li>
* <li>If the source component is itself a {@link UI}, it is returned
* directly.</li>
* <li>Otherwise, the UI is obtained from
* {@code getSource().getUI().get()}.</li>
* </ol>
* <p>
* If the source component is not currently attached to a UI, this method
* throws an {@link IllegalStateException}. This can happen, for example,
* when an initial value is set on a field before it is added to the UI and
* a value-change listener is invoked. If your listener can run while the
* source is detached, use {@code getSource().getUI()} instead, which
* returns an {@link java.util.Optional} and lets you handle the detached
* case explicitly.
* In the common case the source is attached to a UI when the event fires,
* so this method is a convenient shorthand for
* {@code getSource().getUI().get()}.
* <p>
* If none of the above applies and the source component is not currently
* attached to a UI, this method throws an {@link IllegalStateException}.
* This can happen, for example, when an initial value is set on a field
* before it is added to the UI and a value-change listener is invoked. If
* your listener can run while the source is detached, use
* {@code getSource().getUI()} instead, which returns an
* {@link java.util.Optional} and lets you handle the detached case
* explicitly.
*
* @return the UI the source component is attached to, never {@code null}
* @return the UI associated with this event, never {@code null}
* @throws IllegalStateException
* if the source component is not currently attached to a UI
* if the source component is not currently attached to a UI and
* no UI was provided at construction time
*/
public UI getUI() {
T source = getSource();
if (source instanceof UI ui) {
if (ui != null) {
return ui;
}
T source = getSource();
if (source instanceof UI sourceUI) {
return sourceUI;
}
return source.getUI()
.orElseThrow(() -> new IllegalStateException(
"Cannot resolve UI for event source " + source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,14 @@ void getUI_sourceDetached_throwsIllegalStateException() {
assertEquals(true, exception.getMessage().contains("not")
&& exception.getMessage().contains("attached"));
}

@Test
void getUI_explicitlyProvided_returnsProvidedUI() {
MockUI ui = new MockUI();
TestComponent source = new TestComponent();
ComponentEvent<TestComponent> event = new ComponentEvent<>(source,
false, ui);

assertSame(ui, event.getUI());
}
}
Loading