Skip to content

Commit bd1a324

Browse files
author
Nicolai Parlog
committed
Created demonstrations for all nested property features.
1 parent 5b93611 commit bd1a324

File tree

3 files changed

+363
-124
lines changed

3 files changed

+363
-124
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.codefx.libfx.nesting;
2+
3+
import javafx.beans.property.DoubleProperty;
4+
import javafx.beans.property.ObjectProperty;
5+
import javafx.beans.property.Property;
6+
import javafx.beans.property.SimpleDoubleProperty;
7+
import javafx.beans.property.SimpleObjectProperty;
8+
import javafx.beans.property.SimpleStringProperty;
9+
import javafx.beans.property.StringProperty;
10+
11+
/**
12+
* A simple demo class which represents an employee.
13+
*/
14+
class Employee {
15+
16+
/**
17+
* The salary.
18+
*/
19+
private final DoubleProperty salary;
20+
21+
/**
22+
* The address.
23+
*/
24+
private final ObjectProperty<Address> address;
25+
26+
/**
27+
* Creates a new employee with the specified salary.
28+
*
29+
* @param initialSalary
30+
* the employee's initial salary
31+
* @param streetName
32+
* the name of the street the employee lives in
33+
*/
34+
public Employee(double initialSalary, String streetName) {
35+
this.salary = new SimpleDoubleProperty(this, "salary", initialSalary);
36+
this.address = new SimpleObjectProperty<>(this, "address", new Address(streetName));
37+
}
38+
39+
/**
40+
* The salary.
41+
*
42+
* @return the salary as a property
43+
*/
44+
public DoubleProperty salaryProperty() {
45+
return salary;
46+
}
47+
48+
/**
49+
* The address.
50+
*
51+
* @return the address as a property
52+
*/
53+
public Property<Address> addressProperty() {
54+
return address;
55+
}
56+
57+
// #region INNER CLASSES
58+
59+
/**
60+
* A simple demo class which represents an employee's address.
61+
*/
62+
public static class Address {
63+
64+
/**
65+
* The street name.
66+
*/
67+
private final StringProperty streetName;
68+
69+
/**
70+
* Creates a new address with the specified street name.
71+
*
72+
* @param streetName
73+
* the name of the street
74+
*/
75+
public Address(String streetName) {
76+
this.streetName = new SimpleStringProperty(this, "streetName", streetName);
77+
}
78+
79+
/**
80+
* The street name.
81+
*
82+
* @return the street name as a property
83+
*/
84+
public StringProperty streetNameProperty() {
85+
return streetName;
86+
}
87+
88+
}
89+
90+
//#end PRIVATE CLASSES
91+
92+
}
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package org.codefx.libfx.nesting;
2+
3+
import javafx.beans.property.DoubleProperty;
4+
import javafx.beans.property.Property;
5+
import javafx.beans.property.SimpleObjectProperty;
6+
import javafx.beans.property.StringProperty;
7+
8+
import org.codefx.libfx.nesting.property.NestedDoubleProperty;
9+
import org.codefx.libfx.nesting.property.NestedProperty;
10+
import org.codefx.libfx.nesting.property.NestedStringProperty;
11+
12+
/**
13+
* Demonstrates some features of the nesting API.
14+
*/
15+
public class NestedPropertyDemo {
16+
17+
// #region ATTRIBUTES
18+
19+
/**
20+
* The currently selected employee.
21+
*/
22+
private final Property<Employee> currentEmployee;
23+
24+
//#end ATTRIBUTES
25+
26+
// #region CONSTRUCTION & MAIN
27+
28+
/**
29+
* Creates a new demo.
30+
*/
31+
private NestedPropertyDemo() {
32+
this.currentEmployee = new SimpleObjectProperty<>(new Employee(54000, "Some Street"));
33+
}
34+
35+
/**
36+
* Runs this demo.
37+
*
38+
* @param args
39+
* command line arguments (will not be used)
40+
*/
41+
public static void main(String[] args) {
42+
NestedPropertyDemo demo = new NestedPropertyDemo();
43+
44+
demo.nestedPropertyCreation();
45+
demo.nestedPropertyCreationWithBuilder();
46+
demo.nestedPropertyBinding();
47+
demo.nestedPropertyBindingWithMissingInnerObservable();
48+
demo.additionalNestedFeatures();
49+
}
50+
51+
//#end CONSTRUCTION & MAIN
52+
53+
// #region DEMOS
54+
55+
/**
56+
* Demonstrates how to create some nested properties.
57+
*/
58+
private void nestedPropertyCreation() {
59+
print("CREATION");
60+
61+
// all created properties wrap the current employee's street name (which is a String)
62+
63+
// create a Property<String> by starting on the 'currentEmployee' property,
64+
// nest to the employee's address and then to the address' street name;
65+
Property<String> asObjectProperty = Nestings.on(currentEmployee)
66+
.nestProperty(employee -> employee.addressProperty())
67+
.nestProperty(address -> address.streetNameProperty())
68+
.buildProperty();
69+
print("The nested 'Property<String>' has the value: \"" + asObjectProperty.getValue() + "\"");
70+
71+
// now, create a StringProperty instead; note the second nesting step which is different from above
72+
StringProperty asStringProperty = Nestings.on(currentEmployee)
73+
.nestProperty(employee -> employee.addressProperty())
74+
.nestStringProperty(address -> address.streetNameProperty())
75+
.buildProperty();
76+
print("The nested 'StringProperty' has the value: \"" + asStringProperty.getValue() + "\"");
77+
78+
// 'buildProperty' actually returns a 'Nested...Property', which also implements the interface 'Nested'
79+
NestedStringProperty asNestedStringProperty = Nestings.on(currentEmployee)
80+
.nest(employee -> employee.addressProperty())
81+
.nestStringProperty(address -> address.streetNameProperty())
82+
.buildProperty();
83+
print("The 'NestedStringProperty' has the value: \"" + asNestedStringProperty.getValue() + "\"");
84+
85+
// calls to 'nestProperty' can be cut short; note the first nesting step which is different from above
86+
NestedStringProperty withShortcut = Nestings.on(currentEmployee)
87+
.nest(employee -> employee.addressProperty())
88+
.nestStringProperty(address -> address.streetNameProperty())
89+
.buildProperty();
90+
print("The 'NestedStringProperty' (with shortcut) has the value: \"" + withShortcut.getValue() + "\"");
91+
92+
print();
93+
}
94+
95+
/**
96+
* Demonstrates how to create nested properties with builders.
97+
*/
98+
private void nestedPropertyCreationWithBuilder() {
99+
print("CREATION WITH BUILDER");
100+
101+
// after nesting is done, the call to 'buildProperty' can be replaced by 'buildPropertyWithBuilder',
102+
// which allows to set additional values for the created nested property
103+
NestedStringProperty addressWithBeanAndName = Nestings.on(currentEmployee)
104+
.nest(employee -> employee.addressProperty())
105+
.nestStringProperty(address -> address.streetNameProperty())
106+
.buildPropertyWithBuilder()
107+
.setBean(this)
108+
.setName("addressWithBean")
109+
.build();
110+
print("The 'NestedStringProperty' has bean class \""
111+
+ addressWithBeanAndName.getBean().getClass().getSimpleName()
112+
+ "\" and bean name \"" + addressWithBeanAndName.getName() + "\"");
113+
114+
print();
115+
}
116+
117+
/**
118+
* Demonstrates how the binding between the object hierarchy's inner property and the nested property works.
119+
*/
120+
private void nestedPropertyBinding() {
121+
print("NESTED PROPERTY BINDING");
122+
123+
// create a nested property for the current employee's salary
124+
NestedDoubleProperty currentEmployeesSalary = Nestings.on(currentEmployee)
125+
.nestDoubleProperty(employee -> employee.salaryProperty())
126+
.buildProperty();
127+
128+
print("The object hierarchy's value is initially the same as the nested property's value:");
129+
printSalaryValues(currentEmployee, currentEmployeesSalary);
130+
131+
// change the values
132+
currentEmployee.getValue().salaryProperty().set(58000);
133+
print("When the object hierarchy's value is changed, the nested property's value changes as well:");
134+
printSalaryValues(currentEmployee, currentEmployeesSalary);
135+
136+
currentEmployeesSalary.set(62000);
137+
print("When the nested property's value is changed, the object hierarchy's value changes as well:");
138+
printSalaryValues(currentEmployee, currentEmployeesSalary);
139+
140+
// change the object hierarchy
141+
print("\nNow change the object hierarchy so that the inner property is a different one.\n");
142+
143+
Employee oldEmployee = currentEmployee.getValue();
144+
Employee newEmployee = new Employee(42000, "Another Street");
145+
currentEmployee.setValue(newEmployee);
146+
147+
print("When the object hierarchy is changed, the nested property's value changes as well:");
148+
printSalaryValues(currentEmployee, currentEmployeesSalary);
149+
150+
currentEmployee.getValue().salaryProperty().set(45000);
151+
print("When the new object hierarchy's value is changed, the nested property's value changes as well:");
152+
printSalaryValues(currentEmployee, currentEmployeesSalary);
153+
print("But the old hierarchy - in this case the old employee's salary - is unchanged: \""
154+
+ oldEmployee.salaryProperty().get() + "\"");
155+
156+
currentEmployeesSalary.set(48000);
157+
print("Similarly, when the nested property's value is changed, the new object hierarchy's value changes as well:");
158+
printSalaryValues(currentEmployee, currentEmployeesSalary);
159+
print("Again, the old hierarchy - in this case the old employee's salary - is unchanged: \""
160+
+ oldEmployee.salaryProperty().get() + "\"");
161+
162+
print();
163+
}
164+
165+
/**
166+
* Demonstrates how a {@link NestedProperty} behaves when the inner
167+
*/
168+
private void nestedPropertyBindingWithMissingInnerObservable() {
169+
print("NESTED PROPERTY BINDING WHEN INNER OBSERVABLE IS MISSING");
170+
171+
// create a nested property for the current employee's street name
172+
NestedStringProperty currentEmployeesStreetName = Nestings.on(currentEmployee)
173+
.nest(employee -> employee.addressProperty())
174+
.nestStringProperty(address -> address.streetNameProperty())
175+
.buildProperty();
176+
177+
print("Nested property's initial street name: \"" + currentEmployeesStreetName.get() + "\"");
178+
179+
currentEmployee.getValue().addressProperty().setValue(null);
180+
print("The inner observable is now missing (is present: \""
181+
+ currentEmployeesStreetName.isInnerObservablePresent() + "\")");
182+
183+
currentEmployeesStreetName.set("Null Street");
184+
print("The nested property can still be changed: \"" + currentEmployeesStreetName.get() + "\"");
185+
186+
currentEmployee.getValue().addressProperty().setValue(new Employee.Address("New Street"));
187+
print("When a new inner observable is present (\"" + currentEmployeesStreetName.isInnerObservablePresent()
188+
+ "\"), the nested property holds its value: \"" + currentEmployeesStreetName.get() + "\"");
189+
190+
print();
191+
}
192+
193+
/**
194+
* Demonstrates the additional features of the interface {@link Nested}, which is implemented by all nested
195+
* properties.
196+
*/
197+
private void additionalNestedFeatures() {
198+
print("FEATURES OF THE INTERFACE 'NESTED'");
199+
200+
// create a nested property for the current employee's street name
201+
NestedStringProperty currentEmployeesStreetName = Nestings.on(currentEmployee)
202+
.nest(employee -> employee.addressProperty())
203+
.nestStringProperty(address -> address.streetNameProperty())
204+
.buildProperty();
205+
206+
// the interface 'Nested' has a property which indicates whether the inner observable is present;
207+
// one use would be to automatically disable a UI element which displays the property's value;
208+
// in this case, a change listener is added which simply prints the new state
209+
currentEmployeesStreetName.innerObservablePresentProperty()
210+
.addListener(
211+
(observable, oldValue, newValue) -> print("\tInner observable present changed to \"" + newValue
212+
+ "\"."));
213+
214+
print("Set the 'currentEmployee' to null, which means that no inner observable will be present.");
215+
Employee notNullEmployee = currentEmployee.getValue();
216+
currentEmployee.setValue(null);
217+
218+
print("Reset the old employee, which makes an inner observable present.");
219+
currentEmployee.setValue(notNullEmployee);
220+
221+
print("Set a new address for the current employee, which will make *another* inner observable present.");
222+
currentEmployee.getValue().addressProperty().setValue(new Employee.Address("A new street."));
223+
224+
print("Set the current employee's address to null, which means that no inner observable will be present.");
225+
currentEmployee.getValue().addressProperty().setValue(null);
226+
227+
print("Set a new address with a null street name for the current employee, "
228+
+ "which will make an inner observable available. "
229+
+ "That its value will be null does not matter as it is still present.");
230+
currentEmployee.getValue().addressProperty().setValue(new Employee.Address(null));
231+
232+
print();
233+
}
234+
235+
//#end DEMOS
236+
237+
/**
238+
* Prints an empty line to the console.
239+
*/
240+
private static void print() {
241+
System.out.println();
242+
}
243+
244+
/**
245+
* Prints the specified text to the console.
246+
*
247+
* @param text
248+
* the text to print
249+
*/
250+
private static void print(String text) {
251+
System.out.println(text);
252+
}
253+
254+
/**
255+
* Outputs the salary of both specified properties.
256+
*
257+
* @param currentEmployee
258+
* the property holding the current employee; the printed value is accessed by moving through the object
259+
* hierarchy
260+
* @param currentEmployeesSalary
261+
* the nested property holding the current employee's salary; the printed value is accessed by simply
262+
* getting it
263+
*/
264+
private static void printSalaryValues(Property<Employee> currentEmployee, DoubleProperty currentEmployeesSalary) {
265+
String salaries = "\tSalaries: ";
266+
salaries += "\"" + currentEmployee.getValue().salaryProperty().get() + "\" (object hierarchy) ";
267+
salaries += "\"" + currentEmployeesSalary.get() + "\" (nested property) ";
268+
print(salaries);
269+
}
270+
271+
}

0 commit comments

Comments
 (0)