Skip to content

Commit 600f919

Browse files
committed
A variable of type T with listeners of type L.
1 parent 368ff92 commit 600f919

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.scijava.listeners;
2+
3+
import java.util.concurrent.atomic.AtomicReference;
4+
import java.util.function.BiConsumer;
5+
import java.util.function.Consumer;
6+
7+
/**
8+
* A variable of type {@code T}. Accessible via {@link #get()} /
9+
* {@link #set(Object)}. Listeners of type {@code L} are notified when the
10+
* variable is changed (or is set, alternatively).
11+
*
12+
* @author Tobias Pietzsch
13+
*/
14+
public class ListenableVar< T, L >
15+
{
16+
private final AtomicReference< T > ref;
17+
18+
private final Listeners.List< L > listeners;
19+
20+
private final BiConsumer< L, T > notify;
21+
22+
private final boolean notifyWhenSet;
23+
24+
public ListenableVar( final T value, final Consumer< L > notify )
25+
{
26+
this( value, notify, false );
27+
}
28+
29+
public ListenableVar( final T value, final BiConsumer< L, T > notify )
30+
{
31+
this( value, notify, false );
32+
}
33+
34+
public ListenableVar( final T value, final Consumer< L > notify, final boolean notifyWhenSet )
35+
{
36+
this( value, ( l, t ) -> notify.accept( l ), notifyWhenSet );
37+
}
38+
39+
public ListenableVar( final T value, final BiConsumer< L, T > notify, final boolean notifyWhenSet )
40+
{
41+
this.ref = new AtomicReference<>( value );
42+
this.notify = notify;
43+
this.notifyWhenSet = notifyWhenSet;
44+
this.listeners = new Listeners.SynchronizedList<>();
45+
}
46+
47+
public void set( final T value )
48+
{
49+
final T previous = this.ref.getAndSet( value );
50+
if ( notifyWhenSet || !previous.equals( value ) )
51+
listeners.list.forEach( l -> notify.accept( l, value ) );
52+
}
53+
54+
public T get()
55+
{
56+
return ref.get();
57+
}
58+
59+
public Listeners< L > listeners()
60+
{
61+
return listeners;
62+
}
63+
64+
public static < T > ListenableVar< T, Runnable > simple( final T value )
65+
{
66+
return new ListenableVar<>( value, Runnable::run );
67+
}
68+
69+
public static < T > ListenableVar< T, Runnable > simple( final T value, final boolean notifyWhenSet )
70+
{
71+
return new ListenableVar<>( value, Runnable::run, notifyWhenSet );
72+
}
73+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package org.scijava.listeners;
2+
3+
import java.util.concurrent.atomic.AtomicBoolean;
4+
import java.util.concurrent.atomic.AtomicLong;
5+
import org.junit.Assert;
6+
import org.junit.Test;
7+
8+
public class ListenableVarTest
9+
{
10+
@Test
11+
public void testSet()
12+
{
13+
ListenableVar< Long, Runnable > var = ListenableVar.simple( 0L );
14+
Assert.assertEquals( ( long ) var.get(), 0L );
15+
var.set( 10L );
16+
Assert.assertEquals( ( long ) var.get(), 10L );
17+
}
18+
19+
interface ChangeListener
20+
{
21+
void changed();
22+
}
23+
24+
@Test
25+
public void testWithConsumer()
26+
{
27+
ListenableVar< Long, ChangeListener > var = new ListenableVar<>( 0L, ChangeListener::changed );
28+
final AtomicBoolean notified = new AtomicBoolean( false );
29+
var.listeners().add( () -> notified.set( true ) );
30+
var.set( 10l );
31+
32+
Assert.assertTrue( notified.get() );
33+
Assert.assertEquals( ( long ) var.get(), 10L );
34+
}
35+
36+
@Test
37+
public void testNoNotificationForUnchangedValue()
38+
{
39+
ListenableVar< Long, ChangeListener > var = new ListenableVar<>( 10L, ChangeListener::changed );
40+
final AtomicBoolean notified = new AtomicBoolean( false );
41+
var.listeners().add( () -> notified.set( true ) );
42+
var.set( 10L );
43+
Assert.assertFalse( notified.get() );
44+
}
45+
46+
interface ValueListener< T >
47+
{
48+
void valueChanged( T value );
49+
}
50+
51+
@Test
52+
public void testWithBiConsumer()
53+
{
54+
ListenableVar< Long, ValueListener< Long > > var = new ListenableVar<>( 1L, ValueListener::valueChanged );
55+
final AtomicLong notified = new AtomicLong();
56+
var.listeners().add( notified::set );
57+
var.set( 10L );
58+
Assert.assertEquals( notified.get(), 10L );
59+
}
60+
61+
@Test
62+
public void testWithRunnable()
63+
{
64+
ListenableVar< Long, Runnable > var = ListenableVar.simple( 0L );
65+
final AtomicBoolean notified = new AtomicBoolean( false );
66+
var.listeners().add( () -> notified.set( true ) );
67+
var.set( 10L );
68+
Assert.assertTrue( notified.get() );
69+
}
70+
}

0 commit comments

Comments
 (0)