Skip to content

Commit 7566c86

Browse files
author
nicolaiparlog
committed
Merged branch 'feature/listenerhandle' into develop.
2 parents dd878a6 + 4c8c7a0 commit 7566c86

File tree

58 files changed

+3389
-1319
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+3389
-1319
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/demo/java/org/codefx/libfx/control/webview/WebViewHyperlinkListenerDemo.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import javax.swing.event.HyperlinkEvent;
1414
import javax.swing.event.HyperlinkEvent.EventType;
1515

16+
import org.codefx.libfx.listener.handle.ListenerHandle;
17+
1618
/**
1719
* Demonstrates how to use the {@link WebViewHyperlinkListener}.
1820
*/
@@ -110,7 +112,7 @@ private static CheckBox createCancelEventBox() {
110112
private static void manageListener(WebView webView, WebViewHyperlinkListener listener,
111113
BooleanProperty attachedProperty) {
112114
attachedProperty.set(true);
113-
WebViewHyperlinkListenerHandle listenerHandle = WebViews.addHyperlinkListener(webView, listener);
115+
ListenerHandle listenerHandle = WebViews.addHyperlinkListener(webView, listener);
114116

115117
attachedProperty.addListener((obs, wasAttached, isAttached) -> {
116118
if (isAttached) {
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package org.codefx.libfx.listener.handle;
2+
3+
import javafx.beans.Observable;
4+
import javafx.beans.property.Property;
5+
import javafx.beans.property.SimpleStringProperty;
6+
import javafx.beans.value.ChangeListener;
7+
8+
/**
9+
* Demonstrates how to create and use {@link ListenerHandle}s.
10+
*/
11+
@SuppressWarnings("static-method")
12+
public class ListenerHandleDemo {
13+
14+
// #region CONSTRUCTION & MAIN
15+
16+
/**
17+
* Creates a new demo.
18+
*/
19+
private ListenerHandleDemo() {
20+
// nothing to do
21+
}
22+
23+
/**
24+
* Runs this demo.
25+
*
26+
* @param args
27+
* command line arguments (will not be used)
28+
*/
29+
public static void main(String[] args) {
30+
ListenerHandleDemo demo = new ListenerHandleDemo();
31+
32+
demo.createCommonListenerHandle();
33+
demo.createCustomListenerHandle();
34+
demo.attachAndDetach();
35+
}
36+
37+
// #end CONSTRUCTION & MAIN
38+
39+
// #region DEMOS
40+
41+
// construction
42+
43+
/**
44+
* Demonstrates how to simply create a handle for a given observable and listener.
45+
*/
46+
private void createCommonListenerHandle() {
47+
Property<String> property = new SimpleStringProperty();
48+
ChangeListener<String> listener = (obs, oldValue, newValue) -> { /* do nothing for this demo */};
49+
50+
// create the handle; this one is initially attached, i.e. the listener is added to the property
51+
ListenerHandle handle = ListenerHandles.createAttached(property, listener);
52+
// the handle can be used to easily detach and reattach the listener
53+
handle.detach();
54+
handle.attach();
55+
56+
// create a detached handle where the listener was not yet added to the property
57+
handle = ListenerHandles.createDetached(property, listener);
58+
// this one needs to be attached before the listener is executed on changes
59+
handle.attach();
60+
}
61+
62+
/**
63+
* Demonstrates how a listener handle can be created for custom observable implementations with
64+
* {@link ListenerHandleBuilder}.
65+
*/
66+
private void createCustomListenerHandle() {
67+
MyCustomObservable customObservable = new MyCustomObservable();
68+
MyCustomListener customListener = new MyCustomListener();
69+
70+
// use 'ListenerHandles' to get a 'ListenerHandleBuilder' which can be used to create a handle for this
71+
// observable and listener
72+
ListenerHandle handleForCustomClasses = ListenerHandles
73+
.createFor(customObservable, customListener)
74+
.onAttach((observable, listener) -> observable.addListener(listener))
75+
.onDetach((observable, listener) -> observable.removeListener(listener))
76+
.build();
77+
handleForCustomClasses.attach();
78+
}
79+
80+
// attach & detach
81+
82+
/**
83+
* Demonstrates how to add and remove a listener with a {@link ListenerHandle} and compares this to the normal
84+
* approach.
85+
*/
86+
private void attachAndDetach() {
87+
Property<String> observedProperty = new SimpleStringProperty("initial value");
88+
89+
// usually a listener is directly added to the property;
90+
// but if the listener has to be removed later, the reference needs to be stored explicitly
91+
ChangeListener<Object> changePrintingListener = (obs, oldValue, newValue) ->
92+
System.out.println("[LISTENER] Value changed from \"" + oldValue + "\" to \"" + newValue + "\".");
93+
observedProperty.addListener(changePrintingListener);
94+
95+
// this is the alternative with a 'ListenerHandle'
96+
ListenerHandle newValuePrinter = ListenerHandles.createAttached(observedProperty,
97+
(obs, oldValue, newValue) -> System.out.println("[HANDLE] New value: \"" + newValue + "\""));
98+
99+
// now lets change the value to see how it works
100+
observedProperty.setValue("new value");
101+
observedProperty.setValue("even newer value");
102+
103+
// removing a listener needs references to both the observable and the listener;
104+
// depending on the situation this might not be feasible
105+
observedProperty.removeListener(changePrintingListener);
106+
// with a handle, the listener can be removed without giving the caller the possibility tp interact with
107+
// the observable or the listener; it is also a little more readable
108+
newValuePrinter.detach();
109+
110+
// some unobserved changes...
111+
observedProperty.setValue("you won't see this on the console");
112+
observedProperty.setValue("nor this");
113+
114+
// the same as above goes for adding the listener
115+
observedProperty.addListener(changePrintingListener);
116+
newValuePrinter.attach();
117+
118+
// now some more changes
119+
observedProperty.setValue("but you will see this");
120+
observedProperty.setValue("and this");
121+
}
122+
123+
// #end DEMOS
124+
125+
// #region NESTED CLASSES
126+
127+
/**
128+
* Represents a custom observable instance. Note that it is not necessary to implement {@link Observable} (or any
129+
* other interface) in order to use this class with the {@link ListenerHandleBuilder}.
130+
*/
131+
private static class MyCustomObservable {
132+
133+
@SuppressWarnings({ "javadoc", "unused" })
134+
public void addListener(MyCustomListener listener) {
135+
// do nothing - just for demo
136+
}
137+
138+
@SuppressWarnings({ "javadoc", "unused" })
139+
public void removeListener(MyCustomListener listener) {
140+
// do nothing - just for demo
141+
}
142+
143+
}
144+
145+
/**
146+
* Represents a listener for a custom observable instance. Note that it is not necessary to implement
147+
* {@link ChangeListener} (or any other interface) in order to use this class with the {@link ListenerHandleBuilder}
148+
* .
149+
*/
150+
private static class MyCustomListener {
151+
// has no members - just for demo
152+
}
153+
154+
// #end NESTED CLASSES
155+
156+
}

src/main/java/org/codefx/libfx/concurrent/when/ExecuteAlwaysWhen.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
* If the observable is manipulated by several threads during {@code executeWhen()}, this class does not guarantee that
2525
* the first value to pass the condition is the one handed to the action. Depending on the interaction of those threads
2626
* it might be the initial value or one of several which were set by those threads.
27+
* <p>
28+
* Use {@link ExecuteWhen} to build an instance of this class.
2729
*
2830
* @param <T>
2931
* the type the observed {@link ObservableValue}'s wraps
@@ -66,17 +68,17 @@ public class ExecuteAlwaysWhen<T> {
6668
/**
6769
* The condition the {@link #observable}'s value must fulfill for {@link #action} to be executed.
6870
*/
69-
private final Predicate<T> condition;
71+
private final Predicate<? super T> condition;
7072

7173
/**
7274
* The action which will be executed.
7375
*/
74-
private final Consumer<T> action;
76+
private final Consumer<? super T> action;
7577

7678
/**
7779
* The listener which executes {@link #action} and sets {@link #alreadyExecuted} accordingly.
7880
*/
79-
private final ChangeListener<T> listenerWhichExecutesAction;
81+
private final ChangeListener<? super T> listenerWhichExecutesAction;
8082

8183
/**
8284
* Indicates whether {@link #executeWhen()} was already called. If so, it can not be called again.
@@ -107,7 +109,7 @@ public class ExecuteAlwaysWhen<T> {
107109
* @param action
108110
* the action which will be executed
109111
*/
110-
public ExecuteAlwaysWhen(ObservableValue<T> observable, Predicate<T> condition, Consumer<T> action) {
112+
ExecuteAlwaysWhen(ObservableValue<T> observable, Predicate<? super T> condition, Consumer<? super T> action) {
111113
this.observable = observable;
112114
this.condition = condition;
113115
this.action = action;

src/main/java/org/codefx/libfx/concurrent/when/ExecuteOnceWhen.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
* If the observable is manipulated by several threads, this class does not guarantee that the first value to pass the
2424
* condition is the one handed to the action. Depending on the interaction of those threads it might be the initial
2525
* value (the one tested during {@code executeWhen()}) or one of several which were set by those threads.
26-
*
26+
* <p>
27+
* Use {@link ExecuteWhen} to build an instance of this class.
28+
*
2729
* @param <T>
2830
* the type the observed {@link ObservableValue}'s wraps
2931
*/
@@ -56,12 +58,12 @@ public class ExecuteOnceWhen<T> {
5658
/**
5759
* The condition the {@link #observable}'s value must fulfill for {@link #action} to be executed.
5860
*/
59-
private final Predicate<T> condition;
61+
private final Predicate<? super T> condition;
6062

6163
/**
6264
* The action which will be executed.
6365
*/
64-
private final Consumer<T> action;
66+
private final Consumer<? super T> action;
6567

6668
/**
6769
* Indicates whether {@link #action} might still be executed at some point in the future. Is used to prevent the
@@ -93,7 +95,7 @@ public class ExecuteOnceWhen<T> {
9395
* @param action
9496
* the action which will be executed
9597
*/
96-
public ExecuteOnceWhen(ObservableValue<T> observable, Predicate<T> condition, Consumer<T> action) {
98+
ExecuteOnceWhen(ObservableValue<T> observable, Predicate<? super T> condition, Consumer<? super T> action) {
9799
this.observable = observable;
98100
this.condition = condition;
99101
this.action = action;

0 commit comments

Comments
 (0)