Skip to content

Commit 905c4f8

Browse files
author
nicolaiparlog
committed
Nested listeners implement 'ListenerHandle' contract. This is now fully documented and tested.
1 parent 1298759 commit 905c4f8

File tree

6 files changed

+213
-72
lines changed

6 files changed

+213
-72
lines changed

src/main/java/org/codefx/libfx/nesting/listener/NestedChangeListenerHandle.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,24 @@ public class NestedChangeListenerHandle<T> implements NestedListenerHandle {
3232

3333
// #region PROPERTIES
3434

35+
/**
36+
* The {@link Nesting} to whose inner observable the {@link #listener} is attached.
37+
*/
3538
private final Nesting<? extends ObservableValue<T>> nesting;
3639

3740
/**
3841
* The property indicating whether the nesting's inner observable is currently present, i.e. not null.
3942
*/
4043
private final BooleanProperty innerObservablePresent;
4144

45+
/**
46+
* The {@link ChangeListener} which is added to the {@link #nesting}'s inner observable.
47+
*/
4248
private final ChangeListener<? super T> listener;
4349

50+
/**
51+
* Indicates whether the {@link #listener} is currently attached to the {@link #nesting}'s inner observable.
52+
*/
4453
private boolean attached;
4554

4655
//#end PROPERTIES
@@ -80,11 +89,23 @@ public class NestedChangeListenerHandle<T> implements NestedListenerHandle {
8089

8190
// #region ADD & REMOVE
8291

92+
/**
93+
* Adds the {@link #listener} to the specified observable, when indicated by {@link #attached}.
94+
*
95+
* @param observable
96+
* the {@link ObservableValue} to which the listener will be added
97+
*/
8398
private void addIfAttached(ObservableValue<T> observable) {
8499
if (attached)
85100
observable.addListener(listener);
86101
}
87102

103+
/**
104+
* Removes the {@link #listener} from the specified observable.
105+
*
106+
* @param observable
107+
* the {@link ObservableValue} from which the listener will be removed.
108+
*/
88109
private void remove(ObservableValue<T> observable) {
89110
observable.removeListener(listener);
90111
}

src/main/java/org/codefx/libfx/nesting/listener/NestedInvalidationListenerHandle.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,24 @@ public class NestedInvalidationListenerHandle implements NestedListenerHandle {
2929

3030
// #region PROPERTIES
3131

32+
/**
33+
* The {@link Nesting} to whose inner observable the {@link #listener} is attached.
34+
*/
3235
private final Nesting<? extends Observable> nesting;
3336

3437
/**
3538
* The property indicating whether the nesting's inner observable is currently present, i.e. not null.
3639
*/
3740
private final BooleanProperty innerObservablePresent;
3841

42+
/**
43+
* The {@link InvalidationListener} which is added to the {@link #nesting}'s inner observable.
44+
*/
3945
private final InvalidationListener listener;
4046

47+
/**
48+
* Indicates whether the {@link #listener} is currently attached to the {@link #nesting}'s inner observable.
49+
*/
4150
private boolean attached;
4251

4352
//#end PROPERTIES
@@ -75,11 +84,23 @@ public class NestedInvalidationListenerHandle implements NestedListenerHandle {
7584

7685
// #region ADD & REMOVE
7786

87+
/**
88+
* Adds the {@link #listener} to the specified observable, when indicated by {@link #attached}.
89+
*
90+
* @param observable
91+
* the {@link Observable} to which the listener will be added
92+
*/
7893
private void addIfAttached(Observable observable) {
7994
if (attached)
8095
observable.addListener(listener);
8196
}
8297

98+
/**
99+
* Removes the {@link #listener} from the specified observable.
100+
*
101+
* @param observable
102+
* the {@link Observable} from which the listener will be removed.
103+
*/
83104
private void remove(Observable observable) {
84105
observable.removeListener(listener);
85106
}

src/main/java/org/codefx/libfx/nesting/listener/NestedListenerHandle.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
import org.codefx.libfx.listener.ListenerHandle;
44
import org.codefx.libfx.nesting.Nested;
55

6+
/**
7+
* A {@link ListenerHandle} for a listener added to the inner observable of a {@link org.codefx.libfx.nesting.Nesting
8+
* Nesting}.
9+
*
10+
* @see Nested
11+
* @see ListenerHandle
12+
*/
613
public interface NestedListenerHandle extends Nested, ListenerHandle {
7-
14+
// no additional methods defined
815
}

src/test/java/org/codefx/libfx/nesting/listener/AbstractNestedChangeListenerBuilderTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ public void setUp() {
3434
/**
3535
* Creates the tested builder. Each call must return a new instance
3636
*
37+
* @param <T>
38+
* the value wrapped by the nesting's inner observable, which is also the type observed by the change
39+
* listener
3740
* @return a {@link NestedChangeListenerBuilder}
3841
*/
3942
protected abstract <T> NestedChangeListenerBuilder<T, Property<T>> createBuilder();

src/test/java/org/codefx/libfx/nesting/listener/AbstractNestedChangeListenerHandleTest.java

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import static org.junit.Assert.assertFalse;
77
import static org.junit.Assert.assertSame;
88
import static org.junit.Assert.assertTrue;
9+
import static org.junit.Assert.fail;
910
import static org.mockito.Mockito.mock;
1011
import static org.mockito.Mockito.times;
1112
import static org.mockito.Mockito.verify;
1213
import static org.mockito.Mockito.verifyNoMoreInteractions;
13-
import static org.mockito.Mockito.verifyZeroInteractions;
1414
import javafx.beans.property.SimpleStringProperty;
1515
import javafx.beans.property.StringProperty;
1616
import javafx.beans.value.ChangeListener;
@@ -40,10 +40,15 @@ public abstract class AbstractNestedChangeListenerHandleTest {
4040
private NestingAccess.EditableNesting<StringProperty> nesting;
4141

4242
/**
43-
* The added listener. This {@link ChangeListener} will be mocked to verify possible invocations.
43+
* The default listener. This {@link ChangeListener} will be mocked to verify invocations.
4444
*/
4545
private ChangeListener<String> listener;
4646

47+
/**
48+
* A listener which fails the test when being called.
49+
*/
50+
private ChangeListener<String> listenerWhichFailsWhenCalled;
51+
4752
/**
4853
* The tested nested listener, which adds the {@link #listener} to the {@link #nesting}.
4954
*/
@@ -65,11 +70,15 @@ public void setUp() {
6570
innerObservable = new SimpleStringProperty("initial value");
6671
nesting = NestingAccess.EditableNesting.createWithInnerObservable(innerObservable);
6772
listener = mock(ChangeListener.class);
73+
listenerWhichFailsWhenCalled = (obs, oldValue, newValue) -> fail();
6874
}
6975

7076
/**
7177
* Creates a new, initially attached nested listener from the specified nesting and listener.
7278
*
79+
* @param <T>
80+
* the value wrapped by the nesting's inner observable, which is also the type observed by the change
81+
* listener
7382
* @param nesting
7483
* the {@link Nesting} to which the listener will be added
7584
* @param listener
@@ -85,6 +94,9 @@ private <T> NestedChangeListenerHandle<T> createAttachedNestedListenerHandle(
8594
/**
8695
* Creates a new, initially detached nested listener from the specified nesting and listener.
8796
*
97+
* @param <T>
98+
* the value wrapped by the nesting's inner observable, which is also the type observed by the change
99+
* listener
88100
* @param nesting
89101
* the {@link Nesting} to which the listener will be added
90102
* @param listener
@@ -100,10 +112,15 @@ private <T> NestedChangeListenerHandle<T> createDetachedNestedListenerHandle(
100112
/**
101113
* Creates a new nested listener from the specified nesting and listener.
102114
*
115+
* @param <T>
116+
* the value wrapped by the nesting's inner observable, which is also the type observed by the change
117+
* listener
103118
* @param nesting
104119
* the {@link Nesting} to which the listener will be added
105120
* @param listener
106121
* the {@link ChangeListener} which will be added to the nesting
122+
* @param attachedOrDetached
123+
* indicates whether the listener will be initially attached or detached
107124
* @return a new {@link NestedChangeListenerHandle}
108125
*/
109126
protected abstract <T> NestedChangeListenerHandle<T> createNestedListenerHandle(
@@ -140,11 +157,8 @@ public void testObservablePresentAfterConstruction() {
140157
*/
141158
@Test
142159
public void testNoInteractionWithListenerDuringConstruction() {
143-
nestedListenerHandle = createDetachedNestedListenerHandle(nesting, listener);
144-
verifyZeroInteractions(listener);
145-
146-
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
147-
verifyZeroInteractions(listener);
160+
nestedListenerHandle = createDetachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
161+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
148162
}
149163

150164
// changing value
@@ -155,10 +169,8 @@ public void testNoInteractionWithListenerDuringConstruction() {
155169
*/
156170
@Test
157171
public void testChangingValueWhenInitiallyDetached() {
158-
nestedListenerHandle = createDetachedNestedListenerHandle(nesting, listener);
172+
nestedListenerHandle = createDetachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
159173
innerObservable.set("new value");
160-
161-
verifyZeroInteractions(listener);
162174
}
163175

164176
/**
@@ -174,50 +186,29 @@ public void testChangingValue() {
174186
verifyNoMoreInteractions(listener);
175187
}
176188

177-
/**
178-
* Tests whether no listener invocation occurs when the nesting's inner observable's value is changed after the
179-
* listener was detached.
180-
*/
181-
@Test
182-
public void testChangingValueAfterDetach() {
183-
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
184-
// change something while attached
185-
innerObservable.set("new value");
186-
187-
// detach and change something
188-
nestedListenerHandle.detach();
189-
innerObservable.set("new value while detached");
190-
191-
// assert that 'changed' was called only once
192-
verify(listener, times(1)).changed(innerObservable, "initial value", "new value");
193-
verifyNoMoreInteractions(listener);
194-
}
195-
196189
// changing observable
197190

198191
/**
199192
* Tests whether no listener invocation occurs when the nesting's inner observable is changed.
200193
*/
201194
@Test
202195
public void testChangingObservable() {
203-
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
196+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
204197
StringProperty newObservable = new SimpleStringProperty("new observable's initial value");
205198
setNestingObservable(nesting, newObservable);
206199

207200
assertTrue(nestedListenerHandle.isInnerObservablePresent());
208-
verifyZeroInteractions(listener);
209201
}
210202

211203
/**
212204
* Tests whether no listener invocation occurs when the nesting's inner observable is changed to null.
213205
*/
214206
@Test
215207
public void testChangingObservableToNull() {
216-
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
208+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
217209
setNestingObservable(nesting, null);
218210

219211
assertFalse(nestedListenerHandle.isInnerObservablePresent());
220-
verifyZeroInteractions(listener);
221212
}
222213

223214
// changing observable and value
@@ -248,7 +239,7 @@ public void testChangingNewObservablesValue() {
248239
*/
249240
@Test
250241
public void testChangingOldObservablesValue() {
251-
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
242+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
252243
// set a new observable ...
253244
StringProperty newObservable = new SimpleStringProperty("new observable's initial value");
254245
setNestingObservable(nesting, newObservable);
@@ -257,9 +248,65 @@ public void testChangingOldObservablesValue() {
257248

258249
// ... and change the old observable's value
259250
innerObservable.setValue("intial observable's new value");
251+
}
252+
253+
// attach & detach
254+
255+
/**
256+
* Tests whether no listener invocation occurs when the nesting's inner observable's value is changed after the
257+
* listener was detached.
258+
*/
259+
@Test
260+
public void testDetach() {
261+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
262+
nestedListenerHandle.detach();
263+
264+
innerObservable.set("new value while detached");
265+
}
266+
267+
/**
268+
* Tests whether the listener ignores values after it was detached repeatedly.
269+
*/
270+
@Test
271+
public void testMultipleDetach() {
272+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listenerWhichFailsWhenCalled);
273+
nestedListenerHandle.detach();
274+
nestedListenerHandle.detach();
275+
nestedListenerHandle.detach();
276+
277+
innerObservable.set("new value while detached");
278+
}
260279

261-
// assert the listener was not invoked
262-
verifyZeroInteractions(listener);
280+
/**
281+
* Tests whether the listener is correctly invoked when the nesting's observable changes its value after the
282+
* listener was detached and reattached.
283+
*/
284+
@Test
285+
public void testReattach() {
286+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
287+
nestedListenerHandle.detach();
288+
nestedListenerHandle.attach();
289+
innerObservable.set("new value");
290+
291+
// assert that 'changed' was called once and with the right arguments
292+
verify(listener, times(1)).changed(innerObservable, "initial value", "new value");
293+
verifyNoMoreInteractions(listener);
294+
}
295+
296+
/**
297+
* Tests whether the listener is only called once even when attached is called repeatedly.
298+
*/
299+
@Test
300+
public void testMultipleAttach() {
301+
nestedListenerHandle = createAttachedNestedListenerHandle(nesting, listener);
302+
nestedListenerHandle.attach();
303+
nestedListenerHandle.attach();
304+
nestedListenerHandle.attach();
305+
innerObservable.set("new value");
306+
307+
// assert that 'changed' was called only once
308+
verify(listener, times(1)).changed(innerObservable, "initial value", "new value");
309+
verifyNoMoreInteractions(listener);
263310
}
264311

265312
//#end TESTS

0 commit comments

Comments
 (0)