Skip to content

Commit da246c7

Browse files
author
nicolaiparlog
committed
Adapted control property listening to implement 'ListenerHandle'.
1 parent 58c8fbd commit da246c7

15 files changed

+233
-155
lines changed

src/demo/java/org/codefx/libfx/control/ControlPropertyListenerDemo.java

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
import javafx.collections.ObservableMap;
77

88
import org.codefx.libfx.control.properties.ControlProperties;
9-
import org.codefx.libfx.control.properties.ControlPropertyListener;
9+
import org.codefx.libfx.control.properties.ControlPropertyListenerHandle;
1010

1111
/**
12-
* Demonstrates how to use the {@link ControlPropertyListener} and its builder.
12+
* Demonstrates how to use the {@link ControlPropertyListenerHandle} and its builder.
1313
*/
14+
@SuppressWarnings("static-method")
1415
public class ControlPropertyListenerDemo {
1516

1617
// #region CONSTRUCTION & MAIN
@@ -29,13 +30,15 @@ private ControlPropertyListenerDemo() {
2930
* command line arguments (will not be used)
3031
*/
3132
public static void main(String[] args) {
32-
simpleCase();
33-
attachAndDetach();
33+
ControlPropertyListenerDemo demo = new ControlPropertyListenerDemo();
3434

35-
timeNoTypeCheck();
36-
timeWithTypeCheck();
35+
demo.simpleCase();
36+
demo.attachAndDetach();
3737

38-
castVsTypeChecking();
38+
demo.timeNoTypeCheck();
39+
demo.timeWithTypeCheck();
40+
41+
demo.castVsTypeChecking();
3942
}
4043

4144
// #end CONSTRUCTION & MAIN
@@ -45,14 +48,14 @@ public static void main(String[] args) {
4548
/**
4649
* Demonstrates the simple case, in which a value processor is added for some key.
4750
*/
48-
public static void simpleCase() {
51+
private void simpleCase() {
4952
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();
5053

5154
// build and attach the listener
5255
ControlProperties.<String> on(properties)
5356
.forKey("Key")
5457
.processValue(value -> System.out.println(" -> " + value))
55-
.buildAndAttach();
58+
.build();
5659

5760
// set values of the correct type for the correct key
5861
System.out.print("Set \"Value\" for the correct key for the first time: ");
@@ -74,14 +77,14 @@ ControlProperties.<String> on(properties)
7477
/**
7578
* Demonstrates how a listener can be attached and detached.
7679
*/
77-
private static void attachAndDetach() {
80+
private void attachAndDetach() {
7881
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();
7982

8083
// build the listener (but don't attach it yet) and assign it to a variable
81-
ControlPropertyListener listener = ControlProperties.<String> on(properties)
84+
ControlPropertyListenerHandle listener = ControlProperties.<String> on(properties)
8285
.forKey("Key")
8386
.processValue(value -> System.out.println(" -> " + value))
84-
.build();
87+
.buildDetached();
8588

8689
// set a value when the listener is not yet attached
8790
System.out.println(
@@ -105,7 +108,7 @@ private static void attachAndDetach() {
105108
/**
106109
* Measures the time it takes to get a lot of {@link ClassCastException}.
107110
*/
108-
private static void timeNoTypeCheck() {
111+
private void timeNoTypeCheck() {
109112
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();
110113

111114
Consumer<String> unreached = value -> {
@@ -116,7 +119,7 @@ private static void timeNoTypeCheck() {
116119
ControlProperties.<String> on(properties)
117120
.forKey("Key")
118121
.processValue(unreached)
119-
.buildAndAttach();
122+
.build();
120123

121124
// add a couple of values of the wrong type to average the time that takes
122125
Integer valueOfWrongType = 5;
@@ -136,7 +139,7 @@ ControlProperties.<String> on(properties)
136139
/**
137140
* Demonstrates how type checking increases performance if values of an incorrect type are added frequently.
138141
*/
139-
private static void timeWithTypeCheck() {
142+
private void timeWithTypeCheck() {
140143
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();
141144

142145
Consumer<String> unreached = value -> {
@@ -148,7 +151,7 @@ ControlProperties.<String> on(properties)
148151
.forKey("Key")
149152
.forValueType(String.class)
150153
.processValue(unreached)
151-
.buildAndAttach();
154+
.build();
152155

153156
// add a couple of values of the wrong type to average the time that takes
154157
Integer valueOfWrongType = 5;
@@ -174,7 +177,7 @@ ControlProperties.<String> on(properties)
174177
* Some days later: I ran this again and discovered that the time difference is now very measurable and looks
175178
* correct. Perhaps some JVM optimization because I ran it so often?
176179
*/
177-
private static void castVsTypeChecking() {
180+
private void castVsTypeChecking() {
178181
int runs = (int) 1e5;
179182
Object integer = 3;
180183

src/main/java/org/codefx/libfx/control/properties/AbstractControlPropertyListener.java renamed to src/main/java/org/codefx/libfx/control/properties/AbstractControlPropertyListenerHandle.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import javafx.collections.ObservableMap;
77

88
/**
9-
* Abstract superclass to implementations of {@link ControlPropertyListener}. Handles all aspects of listening except
10-
* the actual processing of the value which is delegated to the implementations.
9+
* Abstract superclass to implementations of {@link ControlPropertyListenerHandle}. Handles all aspects of listening
10+
* except the actual processing of the value which is delegated to the implementations.
1111
*/
12-
abstract class AbstractControlPropertyListener implements ControlPropertyListener {
12+
abstract class AbstractControlPropertyListenerHandle implements ControlPropertyListenerHandle {
1313

1414
// #region ATTRIBUTES
1515

@@ -33,14 +33,14 @@ abstract class AbstractControlPropertyListener implements ControlPropertyListene
3333
// #region CONSTRUCTION
3434

3535
/**
36-
* Creates a new listener.
36+
* Creates a new listener handle. Initially detached.
3737
*
3838
* @param properties
3939
* the {@link ObservableMap} holding the properties
4040
* @param key
4141
* the key to which the listener will listen
4242
*/
43-
protected AbstractControlPropertyListener(
43+
protected AbstractControlPropertyListenerHandle(
4444
ObservableMap<Object, Object> properties, Object key) {
4545

4646
Objects.requireNonNull(properties, "The argument 'properties' must not be null.");
@@ -93,10 +93,11 @@ private void processAndRemoveValue(Object value) {
9393

9494
// #end PROCESS VALUE
9595

96-
// #region IMPLEMENTATION OF 'ControlPropertyListener'
96+
// #region IMPLEMENTATION OF 'ControlPropertyListenerHandle'
9797

9898
@Override
9999
public void attach() {
100+
// TODO under threading this can lead to processing the same value twice.
100101
properties.addListener(listener);
101102
if (properties.containsKey(key))
102103
processAndRemoveValue(properties.get(key));
@@ -107,6 +108,6 @@ public void detach() {
107108
properties.removeListener(listener);
108109
}
109110

110-
// #end IMPLEMENTATION OF 'ControlPropertyListener'
111+
// #end IMPLEMENTATION OF 'ControlPropertyListenerHandle'
111112

112113
}

src/main/java/org/codefx/libfx/control/properties/CastingControlPropertyListener.java renamed to src/main/java/org/codefx/libfx/control/properties/CastingControlPropertyListenerHandle.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66
import javafx.collections.ObservableMap;
77

88
/**
9-
* Implementation of {@link ControlPropertyListener} which optimistically casts all values to the expected type. If that
10-
* does not work, the {@link ClassCastException} is caught and ignored.
9+
* Implementation of {@link ControlPropertyListenerHandle} which optimistically casts all values to the expected type.
10+
* If that does not work, the {@link ClassCastException} is caught and ignored.
1111
*
1212
* @param <T>
1313
* the type of values which the listener processes
1414
*/
15-
final class CastingControlPropertyListener<T> extends AbstractControlPropertyListener {
15+
final class CastingControlPropertyListenerHandle<T> extends AbstractControlPropertyListenerHandle {
1616

1717
/**
1818
* The user specified processor for values.
1919
*/
2020
private final Consumer<? super T> valueProcessor;
2121

2222
/**
23-
* Creates a new listener.
23+
* Creates a new listener handle. Initially detached.
2424
*
2525
* @param properties
2626
* the {@link ObservableMap} holding the properties
@@ -29,7 +29,7 @@ final class CastingControlPropertyListener<T> extends AbstractControlPropertyLis
2929
* @param valueProcessor
3030
* the {@link Consumer} for the key's values
3131
*/
32-
CastingControlPropertyListener(
32+
CastingControlPropertyListenerHandle(
3333
ObservableMap<Object, Object> properties, Object key, Consumer<? super T> valueProcessor) {
3434

3535
super(properties, key);

src/main/java/org/codefx/libfx/control/properties/ControlProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
public class ControlProperties {
99

1010
/**
11-
* Creates a builder for a {@link ControlPropertyListener} which observes the specified property map.
11+
* Creates a builder for a {@link ControlPropertyListenerHandle} which observes the specified property map.
1212
* <p>
1313
* Note that it is often necessary to explicitly specify the type parameter {@code T} like so:
1414
*

src/main/java/org/codefx/libfx/control/properties/ControlPropertyListenerBuilder.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
import javafx.collections.ObservableMap;
88

99
/**
10-
* A builder for {@link ControlPropertyListener}.
10+
* A builder for a {@code ControlPropertyListener}. This is no type on its own as explained in
11+
* {@link ControlPropertyListenerHandle}. Such a handle is returned by this builder.
1112
* <p>
1213
* It is best created by calling {@link ControlProperties#on(ObservableMap)} with the control's property map as an
1314
* argument. It is necessary to set a key (with {@link #forKey(Object)}) and a processor function for the value (with
14-
* {@link #processValue(Consumer)}) before calling {@link #build()}.
15+
* {@link #processValue(Consumer)}) before calling {@link #buildDetached()}.
1516
* <p>
1617
* Specifying the value's type with {@link #forValueType(Class)} is optional. If it is done, the built listener will use
1718
* it to check the type of the value before casting it to the type accepted by the value processor. If those types do
@@ -31,12 +32,12 @@ public class ControlPropertyListenerBuilder<T> {
3132
private final ObservableMap<Object, Object> properties;
3233

3334
/**
34-
* The key to which the listener will listen; must no be null by the time {@link #build()} is called.
35+
* The key to which the listener will listen; must no be null by the time {@link #buildDetached()} is called.
3536
*/
3637
private Object key;
3738

3839
/**
39-
* The processor of the key's values; must no be null by the time {@link #build()} is called.
40+
* The processor of the key's values; must no be null by the time {@link #buildDetached()} is called.
4041
*/
4142
private Consumer<? super T> valueProcessor;
4243

@@ -62,7 +63,7 @@ public ControlPropertyListenerBuilder(ObservableMap<Object, Object> properties)
6263
}
6364

6465
/**
65-
* Sets the key. This must be called before {@link #build()}.
66+
* Sets the key. This must be called before {@link #buildDetached()}.
6667
*
6768
* @param key
6869
* the key the built listener will observe
@@ -108,36 +109,39 @@ public ControlPropertyListenerBuilder<T> processValue(Consumer<? super T> valueP
108109
// #region BUILD
109110

110111
/**
111-
* Usability method which calls {@link #build()} and (on the built listener)
112-
* {@link ControlPropertyListener#attach() attach()} before returning the new listener.
112+
* Creates a new property listener according to the arguments specified before and
113+
* {@link ControlPropertyListenerHandle#attach() attaches} it.
113114
*
114-
* @return a {@link ControlPropertyListener}
115+
* @return a {@link ControlPropertyListenerHandle}
116+
* @see ControlPropertyListenerHandle#attach()
115117
*/
116-
public ControlPropertyListener buildAndAttach() {
117-
ControlPropertyListener listener = build();
118+
public ControlPropertyListenerHandle build() {
119+
ControlPropertyListenerHandle listener = buildDetached();
118120
listener.attach();
119121
return listener;
120122
}
121123

122124
/**
123125
* Creates a new property listener according to the arguments specified before.
124126
* <p>
125-
* Note that this builder is not yet added to the map! This can be done by calling
126-
* {@link ControlPropertyListener#attach() attach()} on the returned instance.
127+
* Note that this builder is not yet attached to the map! This can be done by calling
128+
* {@link ControlPropertyListenerHandle#attach() attach()} on the returned instance.
127129
*
128-
* @return a {@link ControlPropertyListener}
130+
* @return a {@link ControlPropertyListenerHandle}
131+
* @see #build()
132+
* @see ControlPropertyListenerHandle#attach()
129133
*/
130-
public ControlPropertyListener build() {
134+
public ControlPropertyListenerHandle buildDetached() {
131135
checkFields();
132136

133137
if (valueType.isPresent())
134-
return new TypeCheckingControlPropertyListener<T>(properties, key, valueType.get(), valueProcessor);
138+
return new TypeCheckingControlPropertyListenerHandle<T>(properties, key, valueType.get(), valueProcessor);
135139
else
136-
return new CastingControlPropertyListener<T>(properties, key, valueProcessor);
140+
return new CastingControlPropertyListenerHandle<T>(properties, key, valueProcessor);
137141
}
138142

139143
/**
140-
* Checks whether the attributes are valid so they can be used to {@link #build()} a listener.
144+
* Checks whether the attributes are valid so they can be used to {@link #buildDetached()} a listener.
141145
*/
142146
private void checkFields() {
143147
if (key == null)
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package org.codefx.libfx.control.properties;
22

3+
import org.codefx.libfx.listener.ListenerHandle;
4+
35
/**
6+
* This is a {@link ListenerHandle handle} on a {@code ControlPropertyListener}, which can be used to {@link #attach()}
7+
* and {@link #detach()} it. The {@code ControlPropertyListener} is no type on its own so it is described here.
8+
* <p>
49
* A control property listener listens to the changes in a Control's
510
* {@link javafx.scene.control.Control#getProperties() propertyMap} and hands values to a value processor (a
611
* {@link java.util.function.Consumer Consumer}) specified during construction.
@@ -11,20 +16,20 @@
1116
* Regardless of whether a value could be cast and processed or not, it will be removed from the map. So if the same
1217
* value is set repeatedly, the specified value processor is called every time.
1318
* <p>
14-
* The listener can be detached and reattached by calling the methods provided by this interface.
15-
* <p>
1619
* A listener is best created with the {@link ControlPropertyListenerBuilder}.
1720
*/
18-
public interface ControlPropertyListener {
21+
public interface ControlPropertyListenerHandle extends ListenerHandle {
1922

2023
/**
2124
* Attaches/adds the listener to the properties map. This immediately processes the key if it is present.
2225
*/
26+
@Override
2327
void attach();
2428

2529
/**
2630
* Detaches/removes the listener from the properties map.
2731
*/
32+
@Override
2833
void detach();
2934

3035
}

src/main/java/org/codefx/libfx/control/properties/TypeCheckingControlPropertyListener.java renamed to src/main/java/org/codefx/libfx/control/properties/TypeCheckingControlPropertyListenerHandle.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import javafx.collections.ObservableMap;
77

88
/**
9-
* A {@link ControlPropertyListener} which uses a {@link Class} instance specified during construction to check whether
10-
* a value is of the correct type.
9+
* A {@link ControlPropertyListenerHandle} which uses a {@link Class} instance specified during construction to check
10+
* whether a value is of the correct type.
1111
*
1212
* @param <T>
1313
* the type of values which the listener processes
1414
*/
15-
final class TypeCheckingControlPropertyListener<T> extends AbstractControlPropertyListener {
15+
final class TypeCheckingControlPropertyListenerHandle<T> extends AbstractControlPropertyListenerHandle {
1616

1717
/**
1818
* The type of values which the listener processes.
@@ -25,7 +25,7 @@ final class TypeCheckingControlPropertyListener<T> extends AbstractControlProper
2525
private final Consumer<? super T> valueProcessor;
2626

2727
/**
28-
* Creates a listener.
28+
* Creates a listener handle. Initially detached.
2929
*
3030
* @param properties
3131
* the {@link ObservableMap} holding the properties
@@ -36,7 +36,7 @@ final class TypeCheckingControlPropertyListener<T> extends AbstractControlProper
3636
* @param valueProcessor
3737
* the {@link Consumer} for the key's values
3838
*/
39-
TypeCheckingControlPropertyListener(
39+
TypeCheckingControlPropertyListenerHandle(
4040
ObservableMap<Object, Object> properties, Object key, Class<T> valueType, Consumer<? super T> valueProcessor) {
4141

4242
super(properties, key);

src/main/java/org/codefx/libfx/control/properties/package-info.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
* .processValue(valueString -> System.out.println(valueString))
2525
* .buildAndAttach();
2626
* </pre>
27-
* It returns an instance of {@link org.codefx.libfx.control.properties.ControlPropertyListener ControlPropertyListener} which can
28-
* be used to easily detach and reattach the listener.
27+
* It returns an instance of {@link org.codefx.libfx.control.properties.ControlPropertyListenerHandle
28+
* ControlPropertyListenerHandle} which can be used to easily detach and reattach the listener.
2929
*
30-
* @see org.codefx.libfx.control.properties.ControlPropertyListener ControlPropertyListener
30+
* @see org.codefx.libfx.control.properties.ControlPropertyListenerHandle ControlPropertyListener
3131
* @see org.codefx.libfx.control.properties.ControlPropertyListenerBuilder ControlPropertyListenerBuilder
3232
*/
3333
package org.codefx.libfx.control.properties;

0 commit comments

Comments
 (0)