11package org .codefx .libfx .nesting .property ;
22
33import java .util .Objects ;
4+ import java .util .Optional ;
5+ import java .util .function .Supplier ;
46
57import javafx .beans .property .Property ;
68
79import org .codefx .libfx .nesting .Nesting ;
10+ import org .codefx .libfx .nesting .property .InnerObservableMissingBehavior .WhenInnerObservableGoesMissing ;
11+ import org .codefx .libfx .nesting .property .InnerObservableMissingBehavior .WhenInnerObservableMissingOnUpdate ;
812
913/**
1014 * Abstract superclass to nested property builders. Collects common builder settings; e.g. for the new property's
1115 * {@link Property#getBean() bean} and {@link Property#getName() name}.
1216 *
17+ * @param <T>
18+ * the most concrete type of the value wrapped by the property which will be built
1319 * @param <O>
1420 * the type of the nesting hierarchy's inner observable (which is a {@link Property})
1521 * @param <P>
1622 * the type of {@link Property} which will be built
23+ * @param <B>
24+ * the most concrete type of this builder (used for fluent API)
1725 */
18- abstract class AbstractNestedPropertyBuilder <O extends Property <?>, P extends NestedProperty <?>> {
26+ abstract class AbstractNestedPropertyBuilder <T , O extends Property <?>, P extends NestedProperty <?>, B extends AbstractNestedPropertyBuilder < T , O , P , B >> {
1927
2028 // #begin PROPERTIES
2129
@@ -24,6 +32,11 @@ abstract class AbstractNestedPropertyBuilder<O extends Property<?>, P extends Ne
2432 */
2533 private final Nesting <O > nesting ;
2634
35+ /**
36+ * The behavior for the case that the inner observable is missing.
37+ */
38+ private final MutableInnerObservableMissingBehavior <T > innerObservableMissingBehavior ;
39+
2740 /**
2841 * The property's future {@link Property#getBean() bean}.
2942 */
@@ -47,6 +60,7 @@ abstract class AbstractNestedPropertyBuilder<O extends Property<?>, P extends Ne
4760 protected AbstractNestedPropertyBuilder (Nesting <O > nesting ) {
4861 Objects .requireNonNull (nesting , "The argument 'nesting' must not be null." );
4962 this .nesting = nesting ;
63+ this .innerObservableMissingBehavior = new MutableInnerObservableMissingBehavior <>();
5064 }
5165
5266 //#end CONSTRUCTOR
@@ -63,76 +77,236 @@ protected AbstractNestedPropertyBuilder(Nesting<O> nesting) {
6377
6478 //#end ABSTRACT METHODS
6579
66- // #begin ACCESSORS
80+ // #begin MUTATORS
6781
6882 /**
69- * @return the nesting which will be used for all nested properties
83+ * Sets the property's {@link Property#getBean() bean}.
84+ *
85+ * @param bean
86+ * the property's future bean
87+ * @return this builder
7088 */
71- protected final Nesting <O > getNesting () {
72- return nesting ;
89+ public final B setBean (Object bean ) {
90+ Objects .requireNonNull (bean , "The argument 'bean' must not be null." );
91+ this .bean = bean ;
92+ return thisAsB ();
7393 }
7494
7595 /**
76- * @return the property's future {@link Property#getBean() bean}.
96+ * Sets the property's {@link Property#getName() name}.
97+ *
98+ * @param name
99+ * the property's future name
100+ * @return this builder
77101 */
78- protected final Object getBean () {
79- return bean ;
102+ public B setName (String name ) {
103+ Objects .requireNonNull (name , "The argument 'name' must not be null." );
104+ this .name = name ;
105+ return thisAsB ();
80106 }
81107
82108 /**
83- * Sets the property's future {@link Property#getBean() bean}.
109+ * The property will keep its value when the inner observable goes missing (see {@link NestedProperty} for details
110+ * on this).
111+ * <p>
112+ * This is the default behavior.
84113 *
85- * @param bean
86- * the property's future bean
114+ * @return this builder
87115 */
88- protected final void setTheBean ( Object bean ) {
89- Objects . requireNonNull ( bean , "The argument 'bean' must not be null." );
90- this . bean = bean ;
116+ public B onInnerObservableMissingKeepValue ( ) {
117+ innerObservableMissingBehavior . whenGoesMissing ( WhenInnerObservableGoesMissing . KEEP_VALUE );
118+ return thisAsB () ;
91119 }
92120
93121 /**
94- * Sets the property's future {@link Property#getBean() bean}.
122+ * The property will change to the default value for the wrapped type when the inner observable goes missing (see
123+ * {@link NestedProperty} for details on this).
124+ * <p>
125+ * For primitive wrapping properties (e.g. {@link NestedIntegerProperty}), this will set the primitive default (e.g.
126+ * 0); for reference wrapping properties this will be null.
95127 *
96- * @param bean
97- * the property's future bean
98128 * @return this builder
99129 */
100- public AbstractNestedPropertyBuilder <O , P > setBean (Object bean ) {
101- Objects .requireNonNull (bean , "The argument 'bean' must not be null." );
102- this .bean = bean ;
103- return this ;
130+ public B onInnerObservableMissingSetDefaultValue () {
131+ innerObservableMissingBehavior .whenGoesMissing (WhenInnerObservableGoesMissing .SET_DEFAULT_VALUE );
132+ return thisAsB ();
104133 }
105134
106135 /**
107- * @return the property's future {@link Property#getBean() bean}.
136+ * The property will change to the specified value when the inner observable goes missing (see
137+ * {@link NestedProperty} for details on this).
138+ * <p>
139+ * This method does not accept null as a value. Call {@link #onInnerObservableMissingSetDefaultValue()} if the
140+ * property should change to the default value for the wrapped type (e.g. 0 for {@link NestedIntegerProperty}).
141+ *
142+ * @param value
143+ * the value to set
144+ * @return this builder
108145 */
109- protected final String getName () {
110- return name ;
146+ public B onInnerObservableMissingSetValue (T value ) {
147+ Objects .requireNonNull (value , "The argument 'value' must not be null." );
148+
149+ innerObservableMissingBehavior .whenGoesMissing (WhenInnerObservableGoesMissing .SET_VALUE_FROM_SUPPLIER );
150+ innerObservableMissingBehavior .valueForMissing (() -> value );
151+ return thisAsB ();
111152 }
112153
113154 /**
114- * Sets the property's future {@link Property#getName() name}.
155+ * The property will change to the value computed by the specified supplier when the inner observable goes missing
156+ * (see {@link NestedProperty} for details on this).
157+ * <p>
158+ * The supplier may produce null in which case primitive wrapping properties will fall back to the type's default
159+ * value (e.g. 0 for {@link NestedIntegerProperty}).
115160 *
116- * @param name
117- * the property's future name
161+ * @param valueSupplier
162+ * the supplier which computes the value to set; may produce null
163+ * @return this builder
118164 */
119- protected final void setTheName (String name ) {
120- Objects .requireNonNull (name , "The argument 'name' must not be null." );
121- this .name = name ;
165+ public B onInnerObservableMissingComputeValue (Supplier <T > valueSupplier ) {
166+ Objects .requireNonNull (valueSupplier , "The argument 'valueSupplier' must not be null." );
167+
168+ innerObservableMissingBehavior .whenGoesMissing (WhenInnerObservableGoesMissing .SET_VALUE_FROM_SUPPLIER );
169+ innerObservableMissingBehavior .valueForMissing (valueSupplier );
170+ return thisAsB ();
122171 }
123172
124173 /**
125- * Sets the property's future {@link Property#getName() name}.
174+ * The property will throw an {@link IllegalStateException} when it is updated (e.g. by calling
175+ * {@link Property#setValue(Object) setValue} or via a binding) while the inner observable is missing (see
176+ * {@link NestedProperty} for details on this).
177+ * <p>
178+ * This is the default behavior.
179+ *
180+ * @return this builder
181+ */
182+ public B onUpdateWhenInnerObservableMissingThrowException () {
183+ innerObservableMissingBehavior .onUpdate (WhenInnerObservableMissingOnUpdate .THROW_EXCEPTION );
184+ return thisAsB ();
185+ }
186+
187+ /**
188+ * The property will accept new values when it is updated (e.g. by calling {@link Property#setValue(Object)
189+ * setValue} or via a binding) while the inner observable is missing (see {@link NestedProperty} for details on
190+ * this).
191+ * <p>
192+ * Once the nesting changes to a new (non-missing) inner observable, the property will change to that observable's
193+ * value.
126194 *
127- * @param name
128- * the property's future name
129195 * @return this builder
130196 */
131- public AbstractNestedPropertyBuilder <O , P > setName (String name ) {
132- setTheName (name );
133- return this ;
197+ public B onUpdateWhenInnerObservableMissingAcceptValues () {
198+ innerObservableMissingBehavior
199+ .onUpdate (WhenInnerObservableMissingOnUpdate .ACCEPT_VALUE_UNTIL_NEXT_INNER_OBSERVABLE );
200+ return thisAsB ();
201+ }
202+
203+ /**
204+ * Performs an unchecked cast to {@code B} which
205+ *
206+ * @return this builder as an instance of {@code B}
207+ */
208+ @ SuppressWarnings ("unchecked" )
209+ private B thisAsB () {
210+ B thisAsB = (B ) this ;
211+ return thisAsB ;
212+ }
213+
214+ // #end MUTATORS
215+
216+ // #begin ACCESSORS FOR SUBCLASSES
217+
218+ /**
219+ * @return the nesting which will be used for all nested properties
220+ */
221+ protected final Nesting <O > getNesting () {
222+ return nesting ;
223+ }
224+
225+ /**
226+ * @return the property's {@link Property#getBean() bean}.
227+ */
228+ protected final Object getBean () {
229+ return bean ;
230+ }
231+
232+ /**
233+ * @return the property's {@link Property#getBean() bean}.
234+ */
235+ protected final String getName () {
236+ return name ;
237+ }
238+
239+ /**
240+ * @return the property's behavior for the case that the inner observable is missing
241+ */
242+ protected final InnerObservableMissingBehavior <T > getInnerObservableMissingBehavior () {
243+ return new ImmutableInnerObservableMissingBehavior <>(innerObservableMissingBehavior );
244+ }
245+
246+ //#end ACCESSORS FOR SUBCLASSES
247+
248+ // #begin NESTED CLASSES
249+
250+ private static class MutableInnerObservableMissingBehavior <T > {
251+
252+ private static final WhenInnerObservableGoesMissing DEFAULT_WHEN_GOES_MISSING = WhenInnerObservableGoesMissing .KEEP_VALUE ;
253+ private static final WhenInnerObservableMissingOnUpdate DEFAULT_ON_UPDATE = WhenInnerObservableMissingOnUpdate .THROW_EXCEPTION ;
254+
255+ private WhenInnerObservableGoesMissing whenGoesMissing ;
256+ private Optional <? extends Supplier <T >> valueForMissing ;
257+ private WhenInnerObservableMissingOnUpdate onUpdate ;
258+
259+ public MutableInnerObservableMissingBehavior () {
260+ this .whenGoesMissing = DEFAULT_WHEN_GOES_MISSING ;
261+ this .valueForMissing = Optional .empty ();
262+ this .onUpdate = DEFAULT_ON_UPDATE ;
263+ }
264+
265+ public void whenGoesMissing (WhenInnerObservableGoesMissing whenGoesMissing ) {
266+ assert whenGoesMissing != null : "The argument 'whenGoesMissing' must not be null." ;
267+ this .whenGoesMissing = whenGoesMissing ;
268+ }
269+
270+ public void valueForMissing (Supplier <T > valueForMissing ) {
271+ this .valueForMissing = Optional .of (valueForMissing );
272+ }
273+
274+ public void onUpdate (WhenInnerObservableMissingOnUpdate onUpdate ) {
275+ assert onUpdate != null : "The argument 'onUpdate' must not be null." ;
276+ this .onUpdate = onUpdate ;
277+ }
278+
279+ }
280+
281+ private static class ImmutableInnerObservableMissingBehavior <T > implements InnerObservableMissingBehavior <T > {
282+
283+ private final WhenInnerObservableGoesMissing whenGoesMissing ;
284+ private final Optional <? extends Supplier <T >> valueForMissing ;
285+ private final WhenInnerObservableMissingOnUpdate onUpdate ;
286+
287+ public ImmutableInnerObservableMissingBehavior (MutableInnerObservableMissingBehavior <T > behavior ) {
288+ this .whenGoesMissing = behavior .whenGoesMissing ;
289+ this .valueForMissing = behavior .valueForMissing ;
290+ this .onUpdate = behavior .onUpdate ;
291+ }
292+
293+ @ Override
294+ public WhenInnerObservableGoesMissing whenGoesMissing () {
295+ return whenGoesMissing ;
296+ }
297+
298+ @ Override
299+ public Optional <? extends Supplier <T >> valueForMissing () {
300+ return valueForMissing ;
301+ }
302+
303+ @ Override
304+ public WhenInnerObservableMissingOnUpdate onUpdate () {
305+ return onUpdate ;
306+ }
307+
134308 }
135309
136- //#end ACCESSORS
310+ // #end NESTED CLASSES
137311
138312}
0 commit comments