Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.beans.binding;
027
028import java.lang.ref.WeakReference;
029
030import javafx.beans.InvalidationListener;
031import javafx.beans.Observable;
032import javafx.beans.value.ObservableBooleanValue;
033import javafx.beans.value.ObservableDoubleValue;
034import javafx.beans.value.ObservableFloatValue;
035import javafx.beans.value.ObservableLongValue;
036import javafx.beans.value.ObservableNumberValue;
037import javafx.beans.value.ObservableObjectValue;
038import javafx.beans.value.ObservableStringValue;
039import javafx.beans.value.ObservableValue;
040import javafx.collections.FXCollections;
041import javafx.collections.ObservableList;
042
043import com.sun.javafx.binding.DoubleConstant;
044import com.sun.javafx.binding.FloatConstant;
045import com.sun.javafx.binding.IntegerConstant;
046import com.sun.javafx.binding.Logging;
047import com.sun.javafx.binding.LongConstant;
048import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
049
050/**
051 * Starting point for a binding that calculates a ternary expression.
052 * <p>
053 * A ternary expression has the basic form
054 * {@code new When(cond).then(value1).otherwise(value2);}. The expression
055 * {@code cond} needs to be a {@link javafx.beans.value.ObservableBooleanValue}.
056 * Based on the value of {@code cond}, the binding contains the value of
057 * {@code value1} (if {@code cond.getValue() == true}) or {@code value2} (if
058 * {@code cond.getValue() == false}). The values {@code value1} and
059 * {@code value2} have to be of the same type. They can be constant values or
060 * implementations of {@link javafx.beans.value.ObservableValue}.
061 */
062public class When {
063    private final ObservableBooleanValue condition;
064
065    /**
066     * The constructor of {@code When}.
067     * 
068     * @param condition
069     *            the condition of the ternary expression
070     */
071    public When(final ObservableBooleanValue condition) {
072        if (condition == null) {
073            throw new NullPointerException("Condition must be specified.");
074        }
075        this.condition = condition;
076    }
077    
078    private static class WhenListener implements InvalidationListener {
079        
080        private final ObservableBooleanValue condition;
081        private final ObservableValue<?> thenValue;
082        private final ObservableValue<?> otherwiseValue;
083        private final WeakReference<Binding<?>> ref;
084        
085        private WhenListener(Binding<?> binding, ObservableBooleanValue condition, ObservableValue<?> thenValue, ObservableValue<?> otherwiseValue) {
086            this.ref = new WeakReference<Binding<?>>(binding);
087            this.condition = condition;
088            this.thenValue = thenValue;
089            this.otherwiseValue = otherwiseValue;
090        }
091
092        @Override
093        public void invalidated(Observable observable) {
094            final Binding<?> binding = ref.get();
095            if (binding == null) {
096                condition.removeListener(this);
097                if (thenValue != null) {
098                    thenValue.removeListener(this);
099                }
100                if (otherwiseValue != null) {
101                    otherwiseValue.removeListener(this);
102                }
103            } else {
104                // short-circuit invalidation. This Binding becomes
105                // only invalid if the condition changes or the
106                // active branch.
107                if (condition.equals(observable) || (binding.isValid() && (condition.get() == observable.equals(thenValue)))) {
108                    binding.invalidate();
109                }
110            }
111        }
112        
113    }
114
115    private static NumberBinding createNumberCondition(
116            final ObservableBooleanValue condition,
117            final ObservableNumberValue thenValue,
118            final ObservableNumberValue otherwiseValue) {
119        if ((thenValue instanceof ObservableDoubleValue) || (otherwiseValue instanceof ObservableDoubleValue)) {
120            return new DoubleBinding() {
121                final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
122                {
123                    condition.addListener(observer);
124                    thenValue.addListener(observer);
125                    otherwiseValue.addListener(observer);
126                }
127
128                @Override
129                public void dispose() {
130                    condition.removeListener(observer);
131                    thenValue.removeListener(observer);
132                    otherwiseValue.removeListener(observer);
133                }
134
135                @Override
136                protected double computeValue() {
137                    final boolean conditionValue = condition.get();
138                    Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
139                    return conditionValue ? thenValue.doubleValue() : otherwiseValue.doubleValue();
140                }
141
142                @Override
143                @ReturnsUnmodifiableCollection
144                public ObservableList<ObservableValue<?>> getDependencies() {
145                    return FXCollections.unmodifiableObservableList(
146                            FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
147                }
148            };
149        } else if ((thenValue instanceof ObservableFloatValue) || (otherwiseValue instanceof ObservableFloatValue)) {
150            return new FloatBinding() {
151                final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
152                {
153                    condition.addListener(observer);
154                    thenValue.addListener(observer);
155                    otherwiseValue.addListener(observer);
156                }
157
158                @Override
159                public void dispose() {
160                    condition.removeListener(observer);
161                    thenValue.removeListener(observer);
162                    otherwiseValue.removeListener(observer);
163                }
164
165                @Override
166                protected float computeValue() {
167                    final boolean conditionValue = condition.get();
168                    Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
169                    return conditionValue ? thenValue.floatValue() : otherwiseValue.floatValue();
170                }
171
172                @Override
173                @ReturnsUnmodifiableCollection
174                public ObservableList<ObservableValue<?>> getDependencies() {
175                    return FXCollections.unmodifiableObservableList(
176                            FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
177                }
178            };
179        } else if ((thenValue instanceof ObservableLongValue) || (otherwiseValue instanceof ObservableLongValue)) {
180            return new LongBinding() {
181                final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
182                {
183                    condition.addListener(observer);
184                    thenValue.addListener(observer);
185                    otherwiseValue.addListener(observer);
186                }
187
188                @Override
189                public void dispose() {
190                    condition.removeListener(observer);
191                    thenValue.removeListener(observer);
192                    otherwiseValue.removeListener(observer);
193                }
194
195                @Override
196                protected long computeValue() {
197                    final boolean conditionValue = condition.get();
198                    Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
199                    return conditionValue ? thenValue.longValue() : otherwiseValue.longValue();
200                }
201
202                @Override
203                @ReturnsUnmodifiableCollection
204                public ObservableList<ObservableValue<?>> getDependencies() {
205                    return FXCollections.unmodifiableObservableList(
206                            FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
207                }
208            };
209        } else {
210            return new IntegerBinding() {
211                final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
212                {
213                    condition.addListener(observer);
214                    thenValue.addListener(observer);
215                    otherwiseValue.addListener(observer);
216                }
217
218                @Override
219                public void dispose() {
220                    condition.removeListener(observer);
221                    thenValue.removeListener(observer);
222                    otherwiseValue.removeListener(observer);
223                }
224
225                @Override
226                protected int computeValue() {
227                    final boolean conditionValue = condition.get();
228                    Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
229                    return conditionValue ? thenValue.intValue(): otherwiseValue.intValue();
230                }
231
232                @Override
233                @ReturnsUnmodifiableCollection
234                public ObservableList<ObservableValue<?>> getDependencies() {
235                    return FXCollections.unmodifiableObservableList(
236                            FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
237                }
238            };
239        }
240    }
241
242    /**
243     * If-then-else expression returning a number.
244     */
245    public class NumberConditionBuilder {
246
247        private ObservableNumberValue thenValue;
248
249        private NumberConditionBuilder(final ObservableNumberValue thenValue) {
250            this.thenValue = thenValue;
251        }
252
253        /**
254         * Defines the {@link javafx.beans.value.ObservableNumberValue} which
255         * value is returned by the ternary expression if the condition is
256         * {@code false}.
257         * 
258         * @param otherwiseValue
259         *            the value
260         * @return the complete {@link DoubleBinding}
261         */
262        public NumberBinding otherwise(ObservableNumberValue otherwiseValue) {
263            if (otherwiseValue == null) {
264                throw new NullPointerException("Value needs to be specified");
265            }
266            return When.createNumberCondition(condition, thenValue, otherwiseValue);
267        }
268
269        /**
270         * Defines a constant value of the ternary expression, that is returned
271         * if the condition is {@code false}.
272         * 
273         * @param otherwiseValue
274         *            the value
275         * @return the complete {@link DoubleBinding}
276         */
277        public DoubleBinding otherwise(double otherwiseValue) {
278            return (DoubleBinding) otherwise(DoubleConstant.valueOf(otherwiseValue));
279        }
280
281        /**
282         * Defines a constant value of the ternary expression, that is returned
283         * if the condition is {@code false}.
284         * 
285         * @param otherwiseValue
286         *            the value
287         * @return the complete {@link NumberBinding}
288         */
289        public NumberBinding otherwise(float otherwiseValue) {
290            return otherwise(FloatConstant.valueOf(otherwiseValue));
291        }
292
293        /**
294         * Defines a constant value of the ternary expression, that is returned
295         * if the condition is {@code false}.
296         * 
297         * @param otherwiseValue
298         *            the value
299         * @return the complete {@link NumberBinding}
300         */
301        public NumberBinding otherwise(long otherwiseValue) {
302            return otherwise(LongConstant.valueOf(otherwiseValue));
303        }
304
305        /**
306         * Defines a constant value of the ternary expression, that is returned
307         * if the condition is {@code false}.
308         * 
309         * @param otherwiseValue
310         *            the value
311         * @return the complete {@link NumberBinding}
312         */
313        public NumberBinding otherwise(int otherwiseValue) {
314            return otherwise(IntegerConstant.valueOf(otherwiseValue));
315        }
316    }
317
318    /**
319     * Defines the {@link javafx.beans.value.ObservableNumberValue} which value
320     * is returned by the ternary expression if the condition is {@code true}.
321     * 
322     * @param thenValue
323     *            the value
324     * @return the intermediate result which still requires the otherwise-branch
325     */
326    public NumberConditionBuilder then(final ObservableNumberValue thenValue) {
327        if (thenValue == null) {
328            throw new NullPointerException("Value needs to be specified");
329        }
330        return new NumberConditionBuilder(thenValue);
331    }
332
333    /**
334     * Defines a constant value of the ternary expression, that is returned if
335     * the condition is {@code true}.
336     * 
337     * @param thenValue
338     *            the value
339     * @return the intermediate result which still requires the otherwise-branch
340     */
341    public NumberConditionBuilder then(double thenValue) {
342        return new NumberConditionBuilder(DoubleConstant.valueOf(thenValue));
343    }
344
345    /**
346     * Defines a constant value of the ternary expression, that is returned if
347     * the condition is {@code true}.
348     * 
349     * @param thenValue
350     *            the value
351     * @return the intermediate result which still requires the otherwise-branch
352     */
353    public NumberConditionBuilder then(float thenValue) {
354        return new NumberConditionBuilder(FloatConstant.valueOf(thenValue));
355    }
356
357    /**
358     * Defines a constant value of the ternary expression, that is returned if
359     * the condition is {@code true}.
360     * 
361     * @param thenValue
362     *            the value
363     * @return the intermediate result which still requires the otherwise-branch
364     */
365    public NumberConditionBuilder then(long thenValue) {
366        return new NumberConditionBuilder(LongConstant.valueOf(thenValue));
367    }
368
369    /**
370     * Defines a constant value of the ternary expression, that is returned if
371     * the condition is {@code true}.
372     * 
373     * @param thenValue
374     *            the value
375     * @return the intermediate result which still requires the otherwise-branch
376     */
377    public NumberConditionBuilder then(int thenValue) {
378        return new NumberConditionBuilder(IntegerConstant.valueOf(thenValue));
379    }
380
381    /**
382     * If-then-else expression returning Boolean.
383     */
384    private class BooleanCondition extends BooleanBinding {
385        private final ObservableBooleanValue trueResult;
386        private final boolean trueResultValue;
387
388        private final ObservableBooleanValue falseResult;
389        private final boolean falseResultValue;
390        
391        private final InvalidationListener observer;
392
393        private BooleanCondition(final ObservableBooleanValue then, final ObservableBooleanValue otherwise) {
394            this.trueResult = then;
395            this.trueResultValue = false;
396            this.falseResult = otherwise;
397            this.falseResultValue = false;
398            this.observer = new WhenListener(this, condition, then, otherwise);
399            condition.addListener(observer);
400            then.addListener(observer);
401            otherwise.addListener(observer);
402        }
403
404        private BooleanCondition(final boolean then, final ObservableBooleanValue otherwise) {
405            this.trueResult = null;
406            this.trueResultValue = then;
407            this.falseResult = otherwise;
408            this.falseResultValue = false;
409            this.observer = new WhenListener(this, condition, null, otherwise);
410            condition.addListener(observer);
411            otherwise.addListener(observer);
412        }
413
414        private BooleanCondition(final ObservableBooleanValue then, final boolean otherwise) {
415            this.trueResult = then;
416            this.trueResultValue = false;
417            this.falseResult = null;
418            this.falseResultValue = otherwise;
419            this.observer = new WhenListener(this, condition, then, null);
420            condition.addListener(observer);
421            then.addListener(observer);
422        }
423
424        private BooleanCondition(final boolean then, final boolean otherwise) {
425            this.trueResult = null;
426            this.trueResultValue = then;
427            this.falseResult = null;
428            this.falseResultValue = otherwise;
429            this.observer = null;
430            super.bind(condition);
431        }
432
433        @Override
434        protected boolean computeValue() {
435            final boolean conditionValue = condition.get();
436            Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
437            return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
438                    : (falseResult != null ? falseResult.get() : falseResultValue);
439        }
440
441        @Override
442        public void dispose() {
443            if (observer == null) {
444                super.unbind(condition);
445            } else {
446                condition.removeListener(observer);
447                if (trueResult != null) {
448                    trueResult.removeListener(observer);
449                }
450                if (falseResult != null) {
451                    falseResult.removeListener(observer);
452                }
453            }
454        }
455
456        @Override
457        @ReturnsUnmodifiableCollection
458        public ObservableList<ObservableValue<?>> getDependencies() {
459            assert condition != null;
460            final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
461            if (trueResult != null) {
462                seq.add(trueResult);
463            }
464            if (falseResult != null) {
465                seq.add(falseResult);
466            }
467            return FXCollections.unmodifiableObservableList(seq);
468        }
469    }
470
471    /**
472     * An intermediate class needed while assembling the ternary expression. It
473     * should not be used in another context.
474     */
475    public class BooleanConditionBuilder {
476
477        private ObservableBooleanValue trueResult;
478        private boolean trueResultValue;
479
480        private BooleanConditionBuilder(final ObservableBooleanValue thenValue) {
481            this.trueResult = thenValue;
482        }
483
484        private BooleanConditionBuilder(final boolean thenValue) {
485            this.trueResultValue = thenValue;
486        }
487
488        /**
489         * Defines the {@link javafx.beans.value.ObservableBooleanValue} which
490         * value is returned by the ternary expression if the condition is
491         * {@code false}.
492         * 
493         * @param otherwiseValue
494         *            the value
495         * @return the complete {@link BooleanBinding}
496         */
497        public BooleanBinding otherwise(final ObservableBooleanValue otherwiseValue) {
498            if (otherwiseValue == null) {
499                throw new NullPointerException("Value needs to be specified");
500            }
501            if (trueResult != null)
502                return new BooleanCondition(trueResult, otherwiseValue);
503            else
504                return new BooleanCondition(trueResultValue, otherwiseValue);
505        }
506
507        /**
508         * Defines a constant value of the ternary expression, that is returned
509         * if the condition is {@code false}.
510         * 
511         * @param otherwiseValue
512         *            the value
513         * @return the complete {@link BooleanBinding}
514         */
515        public BooleanBinding otherwise(final boolean otherwiseValue) {
516            if (trueResult != null)
517                return new BooleanCondition(trueResult, otherwiseValue);
518            else
519                return new BooleanCondition(trueResultValue, otherwiseValue);
520        }
521    }
522
523    /**
524     * Defines the {@link javafx.beans.value.ObservableBooleanValue} which value
525     * is returned by the ternary expression if the condition is {@code true}.
526     * 
527     * @param thenValue
528     *            the value
529     * @return the intermediate result which still requires the otherwise-branch
530     */
531    public BooleanConditionBuilder then(final ObservableBooleanValue thenValue) {
532        if (thenValue == null) {
533            throw new NullPointerException("Value needs to be specified");
534        }
535        return new BooleanConditionBuilder(thenValue);
536    }
537
538    /**
539     * Defines a constant value of the ternary expression, that is returned if
540     * the condition is {@code true}.
541     * 
542     * @param thenValue
543     *            the value
544     * @return the intermediate result which still requires the otherwise-branch
545     */
546    public BooleanConditionBuilder then(final boolean thenValue) {
547        return new BooleanConditionBuilder(thenValue);
548    }
549
550    /**
551     * If-then-else expression returning String.
552     */
553    private class StringCondition extends StringBinding {
554
555        private final ObservableStringValue trueResult;
556        private final String trueResultValue;
557
558        private final ObservableStringValue falseResult;
559        private final String falseResultValue;
560
561        private final InvalidationListener observer;
562
563        private StringCondition(final ObservableStringValue then, final ObservableStringValue otherwise) {
564            this.trueResult = then;
565            this.trueResultValue = "";
566            this.falseResult = otherwise;
567            this.falseResultValue = "";
568            this.observer = new WhenListener(this, condition, then, otherwise);
569            condition.addListener(observer);
570            then.addListener(observer);
571            otherwise.addListener(observer);
572        }
573
574        private StringCondition(final String then, final ObservableStringValue otherwise) {
575            this.trueResult = null;
576            this.trueResultValue = then;
577            this.falseResult = otherwise;
578            this.falseResultValue = "";
579            this.observer = new WhenListener(this, condition, null, otherwise);
580            condition.addListener(observer);
581            otherwise.addListener(observer);
582        }
583
584        private StringCondition(final ObservableStringValue then, final String otherwise) {
585            this.trueResult = then;
586            this.trueResultValue = "";
587            this.falseResult = null;
588            this.falseResultValue = otherwise;
589            this.observer = new WhenListener(this, condition, then, null);
590            condition.addListener(observer);
591            then.addListener(observer);
592        }
593
594        private StringCondition(final String then, final String otherwise) {
595            this.trueResult = null;
596            this.trueResultValue = then;
597            this.falseResult = null;
598            this.falseResultValue = otherwise;
599            this.observer = null;
600            super.bind(condition);
601        }
602
603        @Override
604        protected String computeValue() {
605            final boolean conditionValue = condition.get();
606            Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
607            return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
608                    : (falseResult != null ? falseResult.get() : falseResultValue);
609        }
610
611        @Override
612        public void dispose() {
613            if (observer == null) {
614                super.unbind(condition);
615            } else {
616                condition.removeListener(observer);
617                if (trueResult != null) {
618                    trueResult.removeListener(observer);
619                }
620                if (falseResult != null) {
621                    falseResult.removeListener(observer);
622                }
623            }
624        }
625
626
627        @Override
628        @ReturnsUnmodifiableCollection
629        public ObservableList<ObservableValue<?>> getDependencies() {
630            assert condition != null;
631            final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
632            if (trueResult != null) {
633                seq.add(trueResult);
634            }
635            if (falseResult != null) {
636                seq.add(falseResult);
637            }
638            return FXCollections.unmodifiableObservableList(seq);
639        }
640    }
641
642    /**
643     * An intermediate class needed while assembling the ternary expression. It
644     * should not be used in another context.
645     */
646    public class StringConditionBuilder {
647
648        private ObservableStringValue trueResult;
649        private String trueResultValue;
650
651        private StringConditionBuilder(final ObservableStringValue thenValue) {
652            this.trueResult = thenValue;
653        }
654
655        private StringConditionBuilder(final String thenValue) {
656            this.trueResultValue = thenValue;
657        }
658
659        /**
660         * Defines the {@link javafx.beans.value.ObservableStringValue} which
661         * value is returned by the ternary expression if the condition is
662         * {@code false}.
663         * 
664         * @param otherwiseValue
665         *            the value
666         * @return the complete {@link StringBinding}
667         */
668        public StringBinding otherwise(final ObservableStringValue otherwiseValue) {
669            if (trueResult != null)
670                return new StringCondition(trueResult, otherwiseValue);
671            else
672                return new StringCondition(trueResultValue, otherwiseValue);
673        }
674
675        /**
676         * Defines a constant value of the ternary expression, that is returned
677         * if the condition is {@code false}.
678         * 
679         * @param otherwiseValue
680         *            the value
681         * @return the complete {@link StringBinding}
682         */
683        public StringBinding otherwise(final String otherwiseValue) {
684            if (trueResult != null)
685                return new StringCondition(trueResult, otherwiseValue);
686            else
687                return new StringCondition(trueResultValue, otherwiseValue);
688        }
689    }
690
691    /**
692     * Defines the {@link javafx.beans.value.ObservableStringValue} which value
693     * is returned by the ternary expression if the condition is {@code true}.
694     * 
695     * @param thenValue
696     *            the value
697     * @return the intermediate result which still requires the otherwise-branch
698     */
699    public StringConditionBuilder then(final ObservableStringValue thenValue) {
700        if (thenValue == null) {
701            throw new NullPointerException("Value needs to be specified");
702        }
703        return new StringConditionBuilder(thenValue);
704    }
705
706    /**
707     * Defines a constant value of the ternary expression, that is returned if
708     * the condition is {@code true}.
709     * 
710     * @param thenValue
711     *            the value
712     * @return the intermediate result which still requires the otherwise-branch
713     */
714    public StringConditionBuilder then(final String thenValue) {
715        return new StringConditionBuilder(thenValue);
716    }
717
718    /**
719     * If-then-else expression returning general objects.
720     */
721    private class ObjectCondition<T> extends ObjectBinding<T> {
722
723        private final ObservableObjectValue<T> trueResult;
724        private final T trueResultValue;
725
726        private final ObservableObjectValue<T> falseResult;
727        private final T falseResultValue;
728
729        private final InvalidationListener observer;
730        
731        private ObjectCondition(final ObservableObjectValue<T> then, final ObservableObjectValue<T> otherwise) {
732            this.trueResult = then;
733            this.trueResultValue = null;
734            this.falseResult = otherwise;
735            this.falseResultValue = null;
736            this.observer = new WhenListener(this, condition, then, otherwise);
737            condition.addListener(observer);
738            then.addListener(observer);
739            otherwise.addListener(observer);
740        }
741
742        private ObjectCondition(final T then, final ObservableObjectValue<T> otherwise) {
743            this.trueResult = null;
744            this.trueResultValue = then;
745            this.falseResult = otherwise;
746            this.falseResultValue = null;
747            this.observer = new WhenListener(this, condition, null, otherwise);
748            condition.addListener(observer);
749            otherwise.addListener(observer);
750        }
751
752        private ObjectCondition(final ObservableObjectValue<T> then, final T otherwise) {
753            this.trueResult = then;
754            this.trueResultValue = null;
755            this.falseResult = null;
756            this.falseResultValue = otherwise;
757            this.observer = new WhenListener(this, condition, then, null);
758            condition.addListener(observer);
759            then.addListener(observer);
760        }
761
762        private ObjectCondition(final T then, final T otherwise) {
763            this.trueResult = null;
764            this.trueResultValue = then;
765            this.falseResult = null;
766            this.falseResultValue = otherwise;
767            this.observer = null;
768            super.bind(condition);
769        }
770
771        @Override
772        protected T computeValue() {
773            final boolean conditionValue = condition.get();
774            Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
775            return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
776                    : (falseResult != null ? falseResult.get() : falseResultValue);
777        }
778
779        @Override
780        public void dispose() {
781            if (observer == null) {
782                super.unbind(condition);
783            } else {
784                condition.removeListener(observer);
785                if (trueResult != null) {
786                    trueResult.removeListener(observer);
787                }
788                if (falseResult != null) {
789                    falseResult.removeListener(observer);
790                }
791            }
792        }
793
794
795        @Override
796        @ReturnsUnmodifiableCollection
797        public ObservableList<ObservableValue<?>> getDependencies() {
798            assert condition != null;
799            final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
800            if (trueResult != null) {
801                seq.add(trueResult);
802            }
803            if (falseResult != null) {
804                seq.add(falseResult);
805            }
806            return FXCollections.unmodifiableObservableList(seq);
807        }
808    }
809
810    /**
811     * An intermediate class needed while assembling the ternary expression. It
812     * should not be used in another context.
813     */
814    public class ObjectConditionBuilder<T> {
815
816        private ObservableObjectValue<T> trueResult;
817        private T trueResultValue;
818
819        private ObjectConditionBuilder(final ObservableObjectValue<T> thenValue) {
820            this.trueResult = thenValue;
821        }
822
823        private ObjectConditionBuilder(final T thenValue) {
824            this.trueResultValue = thenValue;
825        }
826
827        /**
828         * Defines the {@link javafx.beans.value.ObservableObjectValue} which
829         * value is returned by the ternary expression if the condition is
830         * {@code false}.
831         * 
832         * @param otherwiseValue
833         *            the value
834         * @return the complete {@link ObjectBinding}
835         */
836        public ObjectBinding<T> otherwise(final ObservableObjectValue<T> otherwiseValue) {
837            if (otherwiseValue == null) {
838                throw new NullPointerException("Value needs to be specified");
839            }
840            if (trueResult != null)
841                return new ObjectCondition<T>(trueResult, otherwiseValue);
842            else
843                return new ObjectCondition<T>(trueResultValue, otherwiseValue);
844        }
845
846        /**
847         * Defines a constant value of the ternary expression, that is returned
848         * if the condition is {@code false}.
849         * 
850         * @param otherwiseValue
851         *            the value
852         * @return the complete {@link ObjectBinding}
853         */
854        public ObjectBinding<T> otherwise(final T otherwiseValue) {
855            if (trueResult != null)
856                return new ObjectCondition<T>(trueResult, otherwiseValue);
857            else
858                return new ObjectCondition<T>(trueResultValue, otherwiseValue);
859        }
860    }
861
862    /**
863     * Defines the {@link javafx.beans.value.ObservableObjectValue} which value
864     * is returned by the ternary expression if the condition is {@code true}.
865     * 
866     * @param thenValue
867     *            the value
868     * @return the intermediate result which still requires the otherwise-branch
869     */
870    public <T> ObjectConditionBuilder<T> then(final ObservableObjectValue<T> thenValue) {
871        if (thenValue == null) {
872            throw new NullPointerException("Value needs to be specified");
873        }
874        return new ObjectConditionBuilder<T>(thenValue);
875    }
876
877    /**
878     * Defines a constant value of the ternary expression, that is returned if
879     * the condition is {@code true}.
880     * 
881     * @param thenValue
882     *            the value
883     * @return the intermediate result which still requires the otherwise-branch
884     */
885    public <T> ObjectConditionBuilder<T> then(final T thenValue) {
886        return new ObjectConditionBuilder<T>(thenValue);
887    }
888
889}