1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.beans.binding;
  27 
  28 import java.lang.ref.WeakReference;
  29 
  30 import javafx.beans.InvalidationListener;
  31 import javafx.beans.NamedArg;
  32 import javafx.beans.Observable;
  33 import javafx.beans.value.ObservableBooleanValue;
  34 import javafx.beans.value.ObservableDoubleValue;
  35 import javafx.beans.value.ObservableFloatValue;
  36 import javafx.beans.value.ObservableLongValue;
  37 import javafx.beans.value.ObservableNumberValue;
  38 import javafx.beans.value.ObservableObjectValue;
  39 import javafx.beans.value.ObservableStringValue;
  40 import javafx.beans.value.ObservableValue;
  41 import javafx.collections.FXCollections;
  42 import javafx.collections.ObservableList;
  43 
  44 import com.sun.javafx.binding.DoubleConstant;
  45 import com.sun.javafx.binding.FloatConstant;
  46 import com.sun.javafx.binding.IntegerConstant;
  47 import com.sun.javafx.binding.Logging;
  48 import com.sun.javafx.binding.LongConstant;
  49 
  50 /**
  51  * Starting point for a binding that calculates a ternary expression.
  52  * <p>
  53  * A ternary expression has the basic form
  54  * {@code new When(cond).then(value1).otherwise(value2);}. The expression
  55  * {@code cond} needs to be a {@link javafx.beans.value.ObservableBooleanValue}.
  56  * Based on the value of {@code cond}, the binding contains the value of
  57  * {@code value1} (if {@code cond.getValue() == true}) or {@code value2} (if
  58  * {@code cond.getValue() == false}). The values {@code value1} and
  59  * {@code value2} have to be of the same type. They can be constant values or
  60  * implementations of {@link javafx.beans.value.ObservableValue}.
  61  * @since JavaFX 2.0
  62  */
  63 public class When {
  64     private final ObservableBooleanValue condition;
  65 
  66     /**
  67      * The constructor of {@code When}.
  68      *
  69      * @param condition
  70      *            the condition of the ternary expression
  71      */
  72     public When(final @NamedArg("condition") ObservableBooleanValue condition) {
  73         if (condition == null) {
  74             throw new NullPointerException("Condition must be specified.");
  75         }
  76         this.condition = condition;
  77     }
  78 
  79     private static class WhenListener implements InvalidationListener {
  80 
  81         private final ObservableBooleanValue condition;
  82         private final ObservableValue<?> thenValue;
  83         private final ObservableValue<?> otherwiseValue;
  84         private final WeakReference<Binding<?>> ref;
  85 
  86         private WhenListener(Binding<?> binding, ObservableBooleanValue condition, ObservableValue<?> thenValue, ObservableValue<?> otherwiseValue) {
  87             this.ref = new WeakReference<Binding<?>>(binding);
  88             this.condition = condition;
  89             this.thenValue = thenValue;
  90             this.otherwiseValue = otherwiseValue;
  91         }
  92 
  93         @Override
  94         public void invalidated(Observable observable) {
  95             final Binding<?> binding = ref.get();
  96             if (binding == null) {
  97                 condition.removeListener(this);
  98                 if (thenValue != null) {
  99                     thenValue.removeListener(this);
 100                 }
 101                 if (otherwiseValue != null) {
 102                     otherwiseValue.removeListener(this);
 103                 }
 104             } else {
 105                 // short-circuit invalidation. This Binding becomes
 106                 // only invalid if the condition changes or the
 107                 // active branch.
 108                 if (condition.equals(observable) || (binding.isValid() && (condition.get() == observable.equals(thenValue)))) {
 109                     binding.invalidate();
 110                 }
 111             }
 112         }
 113 
 114     }
 115 
 116     private static NumberBinding createNumberCondition(
 117             final ObservableBooleanValue condition,
 118             final ObservableNumberValue thenValue,
 119             final ObservableNumberValue otherwiseValue) {
 120         if ((thenValue instanceof ObservableDoubleValue) || (otherwiseValue instanceof ObservableDoubleValue)) {
 121             return new DoubleBinding() {
 122                 final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
 123                 {
 124                     condition.addListener(observer);
 125                     thenValue.addListener(observer);
 126                     otherwiseValue.addListener(observer);
 127                 }
 128 
 129                 @Override
 130                 public void dispose() {
 131                     condition.removeListener(observer);
 132                     thenValue.removeListener(observer);
 133                     otherwiseValue.removeListener(observer);
 134                 }
 135 
 136                 @Override
 137                 protected double computeValue() {
 138                     final boolean conditionValue = condition.get();
 139                     Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 140                     return conditionValue ? thenValue.doubleValue() : otherwiseValue.doubleValue();
 141                 }
 142 
 143                 @Override
 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                 public ObservableList<ObservableValue<?>> getDependencies() {
 174                     return FXCollections.unmodifiableObservableList(
 175                             FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
 176                 }
 177             };
 178         } else if ((thenValue instanceof ObservableLongValue) || (otherwiseValue instanceof ObservableLongValue)) {
 179             return new LongBinding() {
 180                 final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
 181                 {
 182                     condition.addListener(observer);
 183                     thenValue.addListener(observer);
 184                     otherwiseValue.addListener(observer);
 185                 }
 186 
 187                 @Override
 188                 public void dispose() {
 189                     condition.removeListener(observer);
 190                     thenValue.removeListener(observer);
 191                     otherwiseValue.removeListener(observer);
 192                 }
 193 
 194                 @Override
 195                 protected long computeValue() {
 196                     final boolean conditionValue = condition.get();
 197                     Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 198                     return conditionValue ? thenValue.longValue() : otherwiseValue.longValue();
 199                 }
 200 
 201                 @Override
 202                 public ObservableList<ObservableValue<?>> getDependencies() {
 203                     return FXCollections.unmodifiableObservableList(
 204                             FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
 205                 }
 206             };
 207         } else {
 208             return new IntegerBinding() {
 209                 final InvalidationListener observer = new WhenListener(this, condition, thenValue, otherwiseValue);
 210                 {
 211                     condition.addListener(observer);
 212                     thenValue.addListener(observer);
 213                     otherwiseValue.addListener(observer);
 214                 }
 215 
 216                 @Override
 217                 public void dispose() {
 218                     condition.removeListener(observer);
 219                     thenValue.removeListener(observer);
 220                     otherwiseValue.removeListener(observer);
 221                 }
 222 
 223                 @Override
 224                 protected int computeValue() {
 225                     final boolean conditionValue = condition.get();
 226                     Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 227                     return conditionValue ? thenValue.intValue(): otherwiseValue.intValue();
 228                 }
 229 
 230                 @Override
 231                 public ObservableList<ObservableValue<?>> getDependencies() {
 232                     return FXCollections.unmodifiableObservableList(
 233                             FXCollections.<ObservableValue<?>> observableArrayList(condition, thenValue, otherwiseValue));
 234                 }
 235             };
 236         }
 237     }
 238 
 239     /**
 240      * If-then-else expression returning a number.
 241      * @since JavaFX 2.0
 242      */
 243     public class NumberConditionBuilder {
 244 
 245         private ObservableNumberValue thenValue;
 246 
 247         private NumberConditionBuilder(final ObservableNumberValue thenValue) {
 248             this.thenValue = thenValue;
 249         }
 250 
 251         /**
 252          * Defines the {@link javafx.beans.value.ObservableNumberValue} which
 253          * value is returned by the ternary expression if the condition is
 254          * {@code false}.
 255          *
 256          * @param otherwiseValue
 257          *            the value
 258          * @return the complete {@link DoubleBinding}
 259          */
 260         public NumberBinding otherwise(ObservableNumberValue otherwiseValue) {
 261             if (otherwiseValue == null) {
 262                 throw new NullPointerException("Value needs to be specified");
 263             }
 264             return When.createNumberCondition(condition, thenValue, otherwiseValue);
 265         }
 266 
 267         /**
 268          * Defines a constant value of the ternary expression, that is returned
 269          * if the condition is {@code false}.
 270          *
 271          * @param otherwiseValue
 272          *            the value
 273          * @return the complete {@link DoubleBinding}
 274          */
 275         public DoubleBinding otherwise(double otherwiseValue) {
 276             return (DoubleBinding) otherwise(DoubleConstant.valueOf(otherwiseValue));
 277         }
 278 
 279         /**
 280          * Defines a constant value of the ternary expression, that is returned
 281          * if the condition is {@code false}.
 282          *
 283          * @param otherwiseValue
 284          *            the value
 285          * @return the complete {@link NumberBinding}
 286          */
 287         public NumberBinding otherwise(float otherwiseValue) {
 288             return otherwise(FloatConstant.valueOf(otherwiseValue));
 289         }
 290 
 291         /**
 292          * Defines a constant value of the ternary expression, that is returned
 293          * if the condition is {@code false}.
 294          *
 295          * @param otherwiseValue
 296          *            the value
 297          * @return the complete {@link NumberBinding}
 298          */
 299         public NumberBinding otherwise(long otherwiseValue) {
 300             return otherwise(LongConstant.valueOf(otherwiseValue));
 301         }
 302 
 303         /**
 304          * Defines a constant value of the ternary expression, that is returned
 305          * if the condition is {@code false}.
 306          *
 307          * @param otherwiseValue
 308          *            the value
 309          * @return the complete {@link NumberBinding}
 310          */
 311         public NumberBinding otherwise(int otherwiseValue) {
 312             return otherwise(IntegerConstant.valueOf(otherwiseValue));
 313         }
 314     }
 315 
 316     /**
 317      * Defines the {@link javafx.beans.value.ObservableNumberValue} which value
 318      * is returned by the ternary expression if the condition is {@code true}.
 319      *
 320      * @param thenValue
 321      *            the value
 322      * @return the intermediate result which still requires the otherwise-branch
 323      */
 324     public NumberConditionBuilder then(final ObservableNumberValue thenValue) {
 325         if (thenValue == null) {
 326             throw new NullPointerException("Value needs to be specified");
 327         }
 328         return new NumberConditionBuilder(thenValue);
 329     }
 330 
 331     /**
 332      * Defines a constant value of the ternary expression, that is returned if
 333      * the condition is {@code true}.
 334      *
 335      * @param thenValue
 336      *            the value
 337      * @return the intermediate result which still requires the otherwise-branch
 338      */
 339     public NumberConditionBuilder then(double thenValue) {
 340         return new NumberConditionBuilder(DoubleConstant.valueOf(thenValue));
 341     }
 342 
 343     /**
 344      * Defines a constant value of the ternary expression, that is returned if
 345      * the condition is {@code true}.
 346      *
 347      * @param thenValue
 348      *            the value
 349      * @return the intermediate result which still requires the otherwise-branch
 350      */
 351     public NumberConditionBuilder then(float thenValue) {
 352         return new NumberConditionBuilder(FloatConstant.valueOf(thenValue));
 353     }
 354 
 355     /**
 356      * Defines a constant value of the ternary expression, that is returned if
 357      * the condition is {@code true}.
 358      *
 359      * @param thenValue
 360      *            the value
 361      * @return the intermediate result which still requires the otherwise-branch
 362      */
 363     public NumberConditionBuilder then(long thenValue) {
 364         return new NumberConditionBuilder(LongConstant.valueOf(thenValue));
 365     }
 366 
 367     /**
 368      * Defines a constant value of the ternary expression, that is returned if
 369      * the condition is {@code true}.
 370      *
 371      * @param thenValue
 372      *            the value
 373      * @return the intermediate result which still requires the otherwise-branch
 374      */
 375     public NumberConditionBuilder then(int thenValue) {
 376         return new NumberConditionBuilder(IntegerConstant.valueOf(thenValue));
 377     }
 378 
 379     /**
 380      * If-then-else expression returning Boolean.
 381      */
 382     private class BooleanCondition extends BooleanBinding {
 383         private final ObservableBooleanValue trueResult;
 384         private final boolean trueResultValue;
 385 
 386         private final ObservableBooleanValue falseResult;
 387         private final boolean falseResultValue;
 388 
 389         private final InvalidationListener observer;
 390 
 391         private BooleanCondition(final ObservableBooleanValue then, final ObservableBooleanValue otherwise) {
 392             this.trueResult = then;
 393             this.trueResultValue = false;
 394             this.falseResult = otherwise;
 395             this.falseResultValue = false;
 396             this.observer = new WhenListener(this, condition, then, otherwise);
 397             condition.addListener(observer);
 398             then.addListener(observer);
 399             otherwise.addListener(observer);
 400         }
 401 
 402         private BooleanCondition(final boolean then, final ObservableBooleanValue otherwise) {
 403             this.trueResult = null;
 404             this.trueResultValue = then;
 405             this.falseResult = otherwise;
 406             this.falseResultValue = false;
 407             this.observer = new WhenListener(this, condition, null, otherwise);
 408             condition.addListener(observer);
 409             otherwise.addListener(observer);
 410         }
 411 
 412         private BooleanCondition(final ObservableBooleanValue then, final boolean otherwise) {
 413             this.trueResult = then;
 414             this.trueResultValue = false;
 415             this.falseResult = null;
 416             this.falseResultValue = otherwise;
 417             this.observer = new WhenListener(this, condition, then, null);
 418             condition.addListener(observer);
 419             then.addListener(observer);
 420         }
 421 
 422         private BooleanCondition(final boolean then, final boolean otherwise) {
 423             this.trueResult = null;
 424             this.trueResultValue = then;
 425             this.falseResult = null;
 426             this.falseResultValue = otherwise;
 427             this.observer = null;
 428             super.bind(condition);
 429         }
 430 
 431         @Override
 432         protected boolean computeValue() {
 433             final boolean conditionValue = condition.get();
 434             Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 435             return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
 436                     : (falseResult != null ? falseResult.get() : falseResultValue);
 437         }
 438 
 439         @Override
 440         public void dispose() {
 441             if (observer == null) {
 442                 super.unbind(condition);
 443             } else {
 444                 condition.removeListener(observer);
 445                 if (trueResult != null) {
 446                     trueResult.removeListener(observer);
 447                 }
 448                 if (falseResult != null) {
 449                     falseResult.removeListener(observer);
 450                 }
 451             }
 452         }
 453 
 454         @Override
 455         public ObservableList<ObservableValue<?>> getDependencies() {
 456             assert condition != null;
 457             final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
 458             if (trueResult != null) {
 459                 seq.add(trueResult);
 460             }
 461             if (falseResult != null) {
 462                 seq.add(falseResult);
 463             }
 464             return FXCollections.unmodifiableObservableList(seq);
 465         }
 466     }
 467 
 468     /**
 469      * An intermediate class needed while assembling the ternary expression. It
 470      * should not be used in another context.
 471      * @since JavaFX 2.0
 472      */
 473     public class BooleanConditionBuilder {
 474 
 475         private ObservableBooleanValue trueResult;
 476         private boolean trueResultValue;
 477 
 478         private BooleanConditionBuilder(final ObservableBooleanValue thenValue) {
 479             this.trueResult = thenValue;
 480         }
 481 
 482         private BooleanConditionBuilder(final boolean thenValue) {
 483             this.trueResultValue = thenValue;
 484         }
 485 
 486         /**
 487          * Defines the {@link javafx.beans.value.ObservableBooleanValue} which
 488          * value is returned by the ternary expression if the condition is
 489          * {@code false}.
 490          *
 491          * @param otherwiseValue
 492          *            the value
 493          * @return the complete {@link BooleanBinding}
 494          */
 495         public BooleanBinding otherwise(final ObservableBooleanValue otherwiseValue) {
 496             if (otherwiseValue == null) {
 497                 throw new NullPointerException("Value needs to be specified");
 498             }
 499             if (trueResult != null)
 500                 return new BooleanCondition(trueResult, otherwiseValue);
 501             else
 502                 return new BooleanCondition(trueResultValue, otherwiseValue);
 503         }
 504 
 505         /**
 506          * Defines a constant value of the ternary expression, that is returned
 507          * if the condition is {@code false}.
 508          *
 509          * @param otherwiseValue
 510          *            the value
 511          * @return the complete {@link BooleanBinding}
 512          */
 513         public BooleanBinding otherwise(final boolean otherwiseValue) {
 514             if (trueResult != null)
 515                 return new BooleanCondition(trueResult, otherwiseValue);
 516             else
 517                 return new BooleanCondition(trueResultValue, otherwiseValue);
 518         }
 519     }
 520 
 521     /**
 522      * Defines the {@link javafx.beans.value.ObservableBooleanValue} which value
 523      * is returned by the ternary expression if the condition is {@code true}.
 524      *
 525      * @param thenValue
 526      *            the value
 527      * @return the intermediate result which still requires the otherwise-branch
 528      */
 529     public BooleanConditionBuilder then(final ObservableBooleanValue thenValue) {
 530         if (thenValue == null) {
 531             throw new NullPointerException("Value needs to be specified");
 532         }
 533         return new BooleanConditionBuilder(thenValue);
 534     }
 535 
 536     /**
 537      * Defines a constant value of the ternary expression, that is returned if
 538      * the condition is {@code true}.
 539      *
 540      * @param thenValue
 541      *            the value
 542      * @return the intermediate result which still requires the otherwise-branch
 543      */
 544     public BooleanConditionBuilder then(final boolean thenValue) {
 545         return new BooleanConditionBuilder(thenValue);
 546     }
 547 
 548     /**
 549      * If-then-else expression returning String.
 550      */
 551     private class StringCondition extends StringBinding {
 552 
 553         private final ObservableStringValue trueResult;
 554         private final String trueResultValue;
 555 
 556         private final ObservableStringValue falseResult;
 557         private final String falseResultValue;
 558 
 559         private final InvalidationListener observer;
 560 
 561         private StringCondition(final ObservableStringValue then, final ObservableStringValue otherwise) {
 562             this.trueResult = then;
 563             this.trueResultValue = "";
 564             this.falseResult = otherwise;
 565             this.falseResultValue = "";
 566             this.observer = new WhenListener(this, condition, then, otherwise);
 567             condition.addListener(observer);
 568             then.addListener(observer);
 569             otherwise.addListener(observer);
 570         }
 571 
 572         private StringCondition(final String then, final ObservableStringValue otherwise) {
 573             this.trueResult = null;
 574             this.trueResultValue = then;
 575             this.falseResult = otherwise;
 576             this.falseResultValue = "";
 577             this.observer = new WhenListener(this, condition, null, otherwise);
 578             condition.addListener(observer);
 579             otherwise.addListener(observer);
 580         }
 581 
 582         private StringCondition(final ObservableStringValue then, final String otherwise) {
 583             this.trueResult = then;
 584             this.trueResultValue = "";
 585             this.falseResult = null;
 586             this.falseResultValue = otherwise;
 587             this.observer = new WhenListener(this, condition, then, null);
 588             condition.addListener(observer);
 589             then.addListener(observer);
 590         }
 591 
 592         private StringCondition(final String then, final String otherwise) {
 593             this.trueResult = null;
 594             this.trueResultValue = then;
 595             this.falseResult = null;
 596             this.falseResultValue = otherwise;
 597             this.observer = null;
 598             super.bind(condition);
 599         }
 600 
 601         @Override
 602         protected String computeValue() {
 603             final boolean conditionValue = condition.get();
 604             Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 605             return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
 606                     : (falseResult != null ? falseResult.get() : falseResultValue);
 607         }
 608 
 609         @Override
 610         public void dispose() {
 611             if (observer == null) {
 612                 super.unbind(condition);
 613             } else {
 614                 condition.removeListener(observer);
 615                 if (trueResult != null) {
 616                     trueResult.removeListener(observer);
 617                 }
 618                 if (falseResult != null) {
 619                     falseResult.removeListener(observer);
 620                 }
 621             }
 622         }
 623 
 624 
 625         @Override
 626         public ObservableList<ObservableValue<?>> getDependencies() {
 627             assert condition != null;
 628             final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
 629             if (trueResult != null) {
 630                 seq.add(trueResult);
 631             }
 632             if (falseResult != null) {
 633                 seq.add(falseResult);
 634             }
 635             return FXCollections.unmodifiableObservableList(seq);
 636         }
 637     }
 638 
 639     /**
 640      * An intermediate class needed while assembling the ternary expression. It
 641      * should not be used in another context.
 642      * @since JavaFX 2.0
 643      */
 644     public class StringConditionBuilder {
 645 
 646         private ObservableStringValue trueResult;
 647         private String trueResultValue;
 648 
 649         private StringConditionBuilder(final ObservableStringValue thenValue) {
 650             this.trueResult = thenValue;
 651         }
 652 
 653         private StringConditionBuilder(final String thenValue) {
 654             this.trueResultValue = thenValue;
 655         }
 656 
 657         /**
 658          * Defines the {@link javafx.beans.value.ObservableStringValue} which
 659          * value is returned by the ternary expression if the condition is
 660          * {@code false}.
 661          *
 662          * @param otherwiseValue
 663          *            the value
 664          * @return the complete {@link StringBinding}
 665          */
 666         public StringBinding otherwise(final ObservableStringValue otherwiseValue) {
 667             if (trueResult != null)
 668                 return new StringCondition(trueResult, otherwiseValue);
 669             else
 670                 return new StringCondition(trueResultValue, otherwiseValue);
 671         }
 672 
 673         /**
 674          * Defines a constant value of the ternary expression, that is returned
 675          * if the condition is {@code false}.
 676          *
 677          * @param otherwiseValue
 678          *            the value
 679          * @return the complete {@link StringBinding}
 680          */
 681         public StringBinding otherwise(final String otherwiseValue) {
 682             if (trueResult != null)
 683                 return new StringCondition(trueResult, otherwiseValue);
 684             else
 685                 return new StringCondition(trueResultValue, otherwiseValue);
 686         }
 687     }
 688 
 689     /**
 690      * Defines the {@link javafx.beans.value.ObservableStringValue} which value
 691      * is returned by the ternary expression if the condition is {@code true}.
 692      *
 693      * @param thenValue
 694      *            the value
 695      * @return the intermediate result which still requires the otherwise-branch
 696      */
 697     public StringConditionBuilder then(final ObservableStringValue thenValue) {
 698         if (thenValue == null) {
 699             throw new NullPointerException("Value needs to be specified");
 700         }
 701         return new StringConditionBuilder(thenValue);
 702     }
 703 
 704     /**
 705      * Defines a constant value of the ternary expression, that is returned if
 706      * the condition is {@code true}.
 707      *
 708      * @param thenValue
 709      *            the value
 710      * @return the intermediate result which still requires the otherwise-branch
 711      */
 712     public StringConditionBuilder then(final String thenValue) {
 713         return new StringConditionBuilder(thenValue);
 714     }
 715 
 716     /**
 717      * If-then-else expression returning general objects.
 718      */
 719     private class ObjectCondition<T> extends ObjectBinding<T> {
 720 
 721         private final ObservableObjectValue<T> trueResult;
 722         private final T trueResultValue;
 723 
 724         private final ObservableObjectValue<T> falseResult;
 725         private final T falseResultValue;
 726 
 727         private final InvalidationListener observer;
 728 
 729         private ObjectCondition(final ObservableObjectValue<T> then, final ObservableObjectValue<T> otherwise) {
 730             this.trueResult = then;
 731             this.trueResultValue = null;
 732             this.falseResult = otherwise;
 733             this.falseResultValue = null;
 734             this.observer = new WhenListener(this, condition, then, otherwise);
 735             condition.addListener(observer);
 736             then.addListener(observer);
 737             otherwise.addListener(observer);
 738         }
 739 
 740         private ObjectCondition(final T then, final ObservableObjectValue<T> otherwise) {
 741             this.trueResult = null;
 742             this.trueResultValue = then;
 743             this.falseResult = otherwise;
 744             this.falseResultValue = null;
 745             this.observer = new WhenListener(this, condition, null, otherwise);
 746             condition.addListener(observer);
 747             otherwise.addListener(observer);
 748         }
 749 
 750         private ObjectCondition(final ObservableObjectValue<T> then, final T otherwise) {
 751             this.trueResult = then;
 752             this.trueResultValue = null;
 753             this.falseResult = null;
 754             this.falseResultValue = otherwise;
 755             this.observer = new WhenListener(this, condition, then, null);
 756             condition.addListener(observer);
 757             then.addListener(observer);
 758         }
 759 
 760         private ObjectCondition(final T then, final T otherwise) {
 761             this.trueResult = null;
 762             this.trueResultValue = then;
 763             this.falseResult = null;
 764             this.falseResultValue = otherwise;
 765             this.observer = null;
 766             super.bind(condition);
 767         }
 768 
 769         @Override
 770         protected T computeValue() {
 771             final boolean conditionValue = condition.get();
 772             Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue);
 773             return conditionValue ? (trueResult != null ? trueResult.get() : trueResultValue)
 774                     : (falseResult != null ? falseResult.get() : falseResultValue);
 775         }
 776 
 777         @Override
 778         public void dispose() {
 779             if (observer == null) {
 780                 super.unbind(condition);
 781             } else {
 782                 condition.removeListener(observer);
 783                 if (trueResult != null) {
 784                     trueResult.removeListener(observer);
 785                 }
 786                 if (falseResult != null) {
 787                     falseResult.removeListener(observer);
 788                 }
 789             }
 790         }
 791 
 792 
 793         @Override
 794         public ObservableList<ObservableValue<?>> getDependencies() {
 795             assert condition != null;
 796             final ObservableList<ObservableValue<?>> seq = FXCollections.<ObservableValue<?>> observableArrayList(condition);
 797             if (trueResult != null) {
 798                 seq.add(trueResult);
 799             }
 800             if (falseResult != null) {
 801                 seq.add(falseResult);
 802             }
 803             return FXCollections.unmodifiableObservableList(seq);
 804         }
 805     }
 806 
 807     /**
 808      * An intermediate class needed while assembling the ternary expression. It
 809      * should not be used in another context.
 810      * @since JavaFX 2.0
 811      */
 812     public class ObjectConditionBuilder<T> {
 813 
 814         private ObservableObjectValue<T> trueResult;
 815         private T trueResultValue;
 816 
 817         private ObjectConditionBuilder(final ObservableObjectValue<T> thenValue) {
 818             this.trueResult = thenValue;
 819         }
 820 
 821         private ObjectConditionBuilder(final T thenValue) {
 822             this.trueResultValue = thenValue;
 823         }
 824 
 825         /**
 826          * Defines the {@link javafx.beans.value.ObservableObjectValue} which
 827          * value is returned by the ternary expression if the condition is
 828          * {@code false}.
 829          *
 830          * @param otherwiseValue
 831          *            the value
 832          * @return the complete {@link ObjectBinding}
 833          */
 834         public ObjectBinding<T> otherwise(final ObservableObjectValue<T> otherwiseValue) {
 835             if (otherwiseValue == null) {
 836                 throw new NullPointerException("Value needs to be specified");
 837             }
 838             if (trueResult != null)
 839                 return new ObjectCondition<T>(trueResult, otherwiseValue);
 840             else
 841                 return new ObjectCondition<T>(trueResultValue, otherwiseValue);
 842         }
 843 
 844         /**
 845          * Defines a constant value of the ternary expression, that is returned
 846          * if the condition is {@code false}.
 847          *
 848          * @param otherwiseValue
 849          *            the value
 850          * @return the complete {@link ObjectBinding}
 851          */
 852         public ObjectBinding<T> otherwise(final T otherwiseValue) {
 853             if (trueResult != null)
 854                 return new ObjectCondition<T>(trueResult, otherwiseValue);
 855             else
 856                 return new ObjectCondition<T>(trueResultValue, otherwiseValue);
 857         }
 858     }
 859 
 860     /**
 861      * Defines the {@link javafx.beans.value.ObservableObjectValue} which value
 862      * is returned by the ternary expression if the condition is {@code true}.
 863      *
 864      * @param <T> the type of the intermediate result
 865      * @param thenValue
 866      *            the value
 867      * @return the intermediate result which still requires the otherwise-branch
 868      */
 869     public <T> ObjectConditionBuilder<T> then(final ObservableObjectValue<T> thenValue) {
 870         if (thenValue == null) {
 871             throw new NullPointerException("Value needs to be specified");
 872         }
 873         return new ObjectConditionBuilder<T>(thenValue);
 874     }
 875 
 876     /**
 877      * Defines a constant value of the ternary expression, that is returned if
 878      * the condition is {@code true}.
 879      *
 880      * @param <T> the type of the intermediate result
 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 }