Skip to content

Commit de3708d

Browse files
author
nicolaiparlog
committed
'ControlPropertyListenerHandle' implements 'ListenerHandle' contract. This is now fully documented and tested.
1 parent 6d04927 commit de3708d

8 files changed

+150
-66
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ abstract class AbstractControlPropertyListenerHandle implements ControlPropertyL
2828
*/
2929
private final MapChangeListener<Object, Object> listener;
3030

31+
/**
32+
* Indicates whether the {@link #listener} is currently attached to the {@link #properties} map.
33+
*/
34+
private boolean attached;
35+
3136
// #end ATTRIBUTES
3237

3338
// #region CONSTRUCTION
@@ -97,14 +102,18 @@ private void processAndRemoveValue(Object value) {
97102

98103
@Override
99104
public void attach() {
100-
// TODO under threading this can lead to processing the same value twice.
105+
if (attached)
106+
return;
107+
108+
attached = true;
101109
properties.addListener(listener);
102110
if (properties.containsKey(key))
103111
processAndRemoveValue(properties.get(key));
104112
}
105113

106114
@Override
107115
public void detach() {
116+
attached = false;
108117
properties.removeListener(listener);
109118
}
110119

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class ControlProperties {
1111
* 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:
14-
*
14+
*
1515
* <pre>
1616
* ControlProperties.&lt;String&gt; on(...)
1717
* </pre>
@@ -23,7 +23,7 @@ public class ControlProperties {
2323
* @return a {@link ControlPropertyListenerBuilder}
2424
*/
2525
public static <T> ControlPropertyListenerBuilder<T> on(ObservableMap<Object, Object> properties) {
26-
return new ControlPropertyListenerBuilder<T>(properties);
26+
return ControlPropertyListenerBuilder.<T> on(properties);
2727
}
2828

2929
}

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,31 @@ public class ControlPropertyListenerBuilder<T> {
5656
* @param properties
5757
* the properties which will be observed by the built listener
5858
*/
59-
public ControlPropertyListenerBuilder(ObservableMap<Object, Object> properties) {
59+
private ControlPropertyListenerBuilder(ObservableMap<Object, Object> properties) {
6060
Objects.requireNonNull(properties, "The argument 'properties' must not be null.");
6161
this.properties = properties;
6262
this.valueType = Optional.empty();
6363
}
6464

65+
/**
66+
* Creates a builder for a {@link ControlPropertyListenerHandle} which observes the specified property map.
67+
* <p>
68+
* Note that it is often necessary to explicitly specify the type parameter {@code T} like so:
69+
*
70+
* <pre>
71+
* ControlProperties.&lt;String&gt; on(...)
72+
* </pre>
73+
*
74+
* @param <T>
75+
* the type of values which the listener processes
76+
* @param properties
77+
* the {@link ObservableMap} holding the properties
78+
* @return a {@link ControlPropertyListenerBuilder}
79+
*/
80+
public static <T> ControlPropertyListenerBuilder<T> on(ObservableMap<Object, Object> properties) {
81+
return new ControlPropertyListenerBuilder<T>(properties);
82+
}
83+
6584
/**
6685
* Sets the key. This must be called before {@link #buildDetached()}.
6786
*

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66
* This is a {@link ListenerHandle handle} on a {@code ControlPropertyListener}, which can be used to {@link #attach()}
77
* and {@link #detach()} it. The {@code ControlPropertyListener} is no type on its own so it is described here.
88
* <p>
9-
* A control property listener listens to the changes in a Control's
10-
* {@link javafx.scene.control.Control#getProperties() propertyMap} and hands values to a value processor (a
11-
* {@link java.util.function.Consumer Consumer}) specified during construction.
9+
* <h2>ControlPropertyListener</h2> A control property listener listens to the changes in a Control's
10+
* {@link javafx.scene.control.Control#getProperties() propertyMap}. It is created to listen for a specific key and
11+
* hands all new values for that key to a value processor (a {@link java.util.function.Consumer Consumer})..
1212
* <p>
1313
* Even though the property map's value type is {@code Object}, the processor might limit the value's type to any other
1414
* class. If the actual value can not be cast to that type, it is silently ignored.
1515
* <p>
1616
* Regardless of whether a value could be cast and processed or not, it will be removed from the map. So if the same
1717
* value is set repeatedly, the specified value processor is called every time.
1818
* <p>
19-
* A listener is best created with the {@link ControlPropertyListenerBuilder}.
19+
* <h2>ControlPropertyListenerHandle</h2> Listener handles are not thread-safe. See {@link ListenerHandle} for details.
20+
* Additionally, a new value might be processed twice if inserted into a map by another thread while {@link #attach()}
21+
* is executed. This behavior should not be relied upon and might change (i.e. be fixed) in the future.
22+
* <p>
23+
* A listener handle is best created with the {@link ControlPropertyListenerBuilder}.
2024
*/
2125
public interface ControlPropertyListenerHandle extends ListenerHandle {
2226

0 commit comments

Comments
 (0)