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 import java.util.Objects; 30 import java.util.function.BiFunction; 31 import java.util.function.Function; 32 33 import javafx.beans.InvalidationListener; 34 import javafx.beans.NamedArg; 35 import javafx.beans.Observable; 36 import javafx.beans.value.ObservableBooleanValue; 37 import javafx.beans.value.ObservableDoubleValue; 38 import javafx.beans.value.ObservableFloatValue; 39 import javafx.beans.value.ObservableLongValue; 40 import javafx.beans.value.ObservableNumberValue; 41 import javafx.beans.value.ObservableObjectValue; 42 import javafx.beans.value.ObservableStringValue; 43 import javafx.beans.value.ObservableValue; 44 import javafx.collections.FXCollections; 45 import javafx.collections.ObservableList; 46 47 import com.sun.javafx.binding.BooleanConstant; 48 import com.sun.javafx.binding.DoubleConstant; 49 import com.sun.javafx.binding.FloatConstant; 50 import com.sun.javafx.binding.IntegerConstant; 51 import com.sun.javafx.binding.Logging; 52 import com.sun.javafx.binding.LongConstant; 53 import com.sun.javafx.binding.ObjectConstant; 54 import com.sun.javafx.binding.StringConstant; 55 56 /** 57 * Starting point for a binding that calculates a ternary expression. 58 * <p> 59 * A ternary expression has the basic form 60 * {@code new When(cond).then(value1).otherwise(value2);}. The expression 61 * {@code cond} needs to be an {@link javafx.beans.value.ObservableBooleanValue}. 62 * Based on the value of {@code cond}, the binding contains the value of 63 * {@code value1} (if {@code cond.getValue() == true}) or {@code value2} (if 64 * {@code cond.getValue() == false}). The values {@code value1} and 65 * {@code value2} have to be of the same type. They can be constant values or 66 * implementations of {@link javafx.beans.value.ObservableValue}. 67 * @since JavaFX 2.0 68 */ 69 public class When { 70 71 private static final String VALUE_MUST_BE_SPECIFIED = "Value must be specified"; 72 73 private final ObservableBooleanValue condition; 74 75 /** 76 * The constructor of {@code When}. 77 * 78 * @param condition 79 * the condition of the ternary expression 80 */ 81 public When(final @NamedArg("condition") ObservableBooleanValue condition) { 82 this.condition = Objects.requireNonNull(condition, "Condition must be specified."); 83 } 84 85 private static class WhenListener implements InvalidationListener { 86 87 private final ObservableBooleanValue condition; 88 private final ObservableValue<?> thenValue; 89 private final ObservableValue<?> otherwiseValue; 90 private final WeakReference<Binding<?>> ref; 91 92 private WhenListener(Binding<?> binding, ObservableBooleanValue condition, ObservableValue<?> thenValue, 93 ObservableValue<?> otherwiseValue) { 94 this.ref = new WeakReference<Binding<?>>(binding); 95 this.condition = condition; 96 this.thenValue = thenValue; 97 this.otherwiseValue = otherwiseValue; 98 } 99 100 @Override 101 public void invalidated(Observable observable) { 102 final Binding<?> binding = ref.get(); 103 if (binding == null) { 104 condition.removeListener(this); 105 if (thenValue != null) { 106 thenValue.removeListener(this); 107 } 108 if (otherwiseValue != null) { 109 otherwiseValue.removeListener(this); 110 } 111 } else { 112 // short-circuit invalidation. This Binding becomes 113 // only invalid if the condition changes or the 114 // active branch. 115 if (condition.equals(observable) || (binding.isValid() && (condition.get() == observable.equals(thenValue)))) { 116 binding.invalidate(); 117 } 118 } 119 } 120 } 121 122 private abstract class ConditionHolder<V, B extends Binding<V>> { 123 124 protected B binding; 125 private ObservableValue<V> thenValue; 126 private ObservableValue<V> otherwiseValue; 127 private InvalidationListener observer; 128 129 protected void registerListeners(final ObservableValue<V> then, final ObservableValue<V> otherwise) { 130 thenValue = then; 131 otherwiseValue = otherwise; 132 observer = new WhenListener(binding, condition, thenValue, otherwiseValue); 133 condition.addListener(observer); 134 thenValue.addListener(observer); 135 otherwiseValue.addListener(observer); 136 } 137 138 // TODO: unnecessary - can access field directly 139 protected B getBinding() { 140 return binding; 141 } 142 143 protected ObservableList<ObservableValue<?>> getDependencies() { 144 return FXCollections.unmodifiableObservableList( 145 FXCollections.observableArrayList(condition, thenValue, otherwiseValue)); 146 } 147 148 protected V computeValue() { 149 final boolean conditionValue = condition.get(); 150 Logging.getLogger().finest("Condition of ternary binding expression was evaluated: {0}", conditionValue); 151 return conditionValue ? thenValue.getValue() : otherwiseValue.getValue(); 152 } 153 154 protected void dispose() { 155 condition.removeListener(observer); 156 thenValue.removeListener(observer); 157 otherwiseValue.removeListener(observer); 158 } 159 } 160 161 private class DoubleConditionHolder extends ConditionHolder<Number, DoubleBinding> { 162 163 protected DoubleConditionHolder(ObservableNumberValue then, ObservableNumberValue otherwise) { 164 binding = new DoubleBinding() { 165 166 @Override 167 protected double computeValue() { 168 return DoubleConditionHolder.super.computeValue().doubleValue(); 169 } 170 171 @Override 172 public ObservableList<?> getDependencies() { 173 return DoubleConditionHolder.super.getDependencies(); 174 } 175 176 @Override 177 public void dispose() { 178 DoubleConditionHolder.super.dispose(); 179 } 180 }; 181 registerListeners(then, otherwise); 182 } 183 } 184 185 /** 186 * If-then-else expression returning a number. 187 * @since JavaFX 2.0 188 */ 189 public class NumberConditionBuilder { 190 191 private ObservableNumberValue thenValue; 192 193 private NumberConditionBuilder(final ObservableNumberValue thenValue) { 194 this.thenValue = thenValue; 195 } 196 197 /** 198 * Defines the {@link javafx.beans.value.ObservableNumberValue} whose 199 * value is returned by the ternary expression if the condition is 200 * {@code false}. 201 * 202 * @param otherwiseValue 203 * the value 204 * @return the complete {@link DoubleBinding} 205 */ 206 public NumberBinding otherwise(ObservableNumberValue otherwiseValue) { 207 Objects.requireNonNull(otherwiseValue, VALUE_MUST_BE_SPECIFIED); 208 // Arranged by widening conventions 209 if ((thenValue instanceof ObservableDoubleValue) || (otherwiseValue instanceof ObservableDoubleValue)) { 210 return new DoubleConditionHolder(thenValue, otherwiseValue).getBinding(); 211 } else if ((thenValue instanceof ObservableFloatValue) || (otherwiseValue instanceof ObservableFloatValue)) { 212 return new DoubleConditionHolder(thenValue, otherwiseValue).getBinding(); 213 } else if ((thenValue instanceof ObservableLongValue) || (otherwiseValue instanceof ObservableLongValue)) { 214 return new DoubleConditionHolder(thenValue, otherwiseValue).getBinding(); 215 } else { 216 return new DoubleConditionHolder(thenValue, otherwiseValue).getBinding(); 217 } 218 } 219 220 /** 221 * Defines a constant value of the ternary expression that is returned 222 * if the condition is {@code false}. 223 * 224 * @param otherwiseValue 225 * the value 226 * @return the complete {@link DoubleBinding} 227 */ 228 public DoubleBinding otherwise(double otherwiseValue) { 229 return (DoubleBinding) otherwise(DoubleConstant.valueOf(otherwiseValue)); 230 } 231 232 /** 233 * Defines a constant value of the ternary expression that is returned 234 * if the condition is {@code false}. 235 * 236 * @param otherwiseValue 237 * the value 238 * @return the complete {@link NumberBinding} 239 */ 240 public NumberBinding otherwise(float otherwiseValue) { 241 return otherwise(FloatConstant.valueOf(otherwiseValue)); // TODO: add note about runtime type of binding? 242 } 243 244 /** 245 * Defines a constant value of the ternary expression that is returned 246 * if the condition is {@code false}. 247 * 248 * @param otherwiseValue 249 * the value 250 * @return the complete {@link NumberBinding} 251 */ 252 public NumberBinding otherwise(long otherwiseValue) { 253 return otherwise(LongConstant.valueOf(otherwiseValue)); 254 } 255 256 /** 257 * Defines a constant value of the ternary expression that is returned 258 * if the condition is {@code false}. 259 * 260 * @param otherwiseValue 261 * the value 262 * @return the complete {@link NumberBinding} 263 */ 264 public NumberBinding otherwise(int otherwiseValue) { 265 return otherwise(IntegerConstant.valueOf(otherwiseValue)); 266 } 267 } 268 269 /** 270 * Defines the {@link javafx.beans.value.ObservableNumberValue} which value 271 * is returned by the ternary expression if the condition is {@code true}. 272 * 273 * @param thenValue 274 * the value 275 * @return the intermediate result which still requires the otherwise-branch 276 */ 277 public NumberConditionBuilder then(final ObservableNumberValue thenValue) { 278 Objects.requireNonNull(thenValue, VALUE_MUST_BE_SPECIFIED); 279 return new NumberConditionBuilder(thenValue); 280 } 281 282 /** 283 * Defines a constant value of the ternary expression that is returned if 284 * the condition is {@code true}. 285 * 286 * @param thenValue 287 * the value 288 * @return the intermediate result which still requires the otherwise-branch 289 */ 290 public NumberConditionBuilder then(double thenValue) { 291 return then(DoubleConstant.valueOf(thenValue)); 292 } 293 294 /** 295 * Defines a constant value of the ternary expression that is returned if 296 * the condition is {@code true}. 297 * 298 * @param thenValue 299 * the value 300 * @return the intermediate result which still requires the otherwise-branch 301 */ 302 public NumberConditionBuilder then(float thenValue) { 303 return then(FloatConstant.valueOf(thenValue)); 304 } 305 306 /** 307 * Defines a constant value of the ternary expression that is returned if 308 * the condition is {@code true}. 309 * 310 * @param thenValue 311 * the value 312 * @return the intermediate result which still requires the otherwise-branch 313 */ 314 public NumberConditionBuilder then(long thenValue) { 315 return then(LongConstant.valueOf(thenValue)); 316 } 317 318 /** 319 * Defines a constant value of the ternary expression that is returned if 320 * 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(int thenValue) { 327 return then(IntegerConstant.valueOf(thenValue)); 328 } 329 330 private class BooleanConditionHolder extends ConditionHolder<Boolean, BooleanBinding> { 331 332 private BooleanConditionHolder(ObservableValue<Boolean> then, ObservableValue<Boolean> otherwise) { 333 binding = new BooleanBinding() { 334 335 @Override 336 protected boolean computeValue() { 337 return BooleanConditionHolder.super.computeValue(); 338 } 339 340 @Override 341 public void dispose() { 342 BooleanConditionHolder.super.dispose(); 343 } 344 345 @Override 346 public ObservableList<ObservableValue<?>> getDependencies() { 347 return BooleanConditionHolder.super.getDependencies(); 348 } 349 }; 350 registerListeners(then, otherwise); 351 } 352 } 353 354 private abstract class ConditionBuilder<V, B extends Binding<V>> { 355 356 private ObservableValue<V> thenValue; 357 protected BiFunction<ObservableValue<V>, ObservableValue<V>, ? extends ConditionHolder<V, B>> conditionHolderGenerator; 358 protected Function<V, ObservableValue<V>> primitiveWrapper; 359 360 protected ConditionBuilder(final ObservableValue<V> thenValue) { 361 this.thenValue = thenValue; 362 } 363 364 public B otherwise(final ObservableValue<V> otherwiseValue) { 365 Objects.requireNonNull(otherwiseValue, VALUE_MUST_BE_SPECIFIED); 366 return conditionHolderGenerator.apply(thenValue, otherwiseValue).getBinding(); 367 } 368 369 public B otherwise(final V otherwiseValue) { 370 return otherwise(primitiveWrapper.apply(otherwiseValue)); 371 } 372 } 373 374 /** 375 * An intermediate class needed while assembling the ternary expression. It 376 * should not be used in another context. 377 * @since JavaFX 2.0 378 */ 379 public class BooleanConditionBuilder2 extends ConditionBuilder<Boolean, BooleanBinding> { 380 381 private BooleanConditionBuilder2(ObservableBooleanValue then) { 382 super(then); 383 conditionHolderGenerator = (thenValue, otherwiseValue) -> new BooleanConditionHolder(thenValue, otherwiseValue); 384 primitiveWrapper = otherwiseValue -> BooleanConstant.valueOf(otherwiseValue); 385 } 386 387 /** 388 * Defines the {@link javafx.beans.value.ObservableBooleanValue} whose 389 * value is returned by the ternary expression if the condition is 390 * {@code false}. 391 * 392 * @param otherwiseValue 393 * the value 394 * @return the complete {@link BooleanBinding} 395 */ 396 public BooleanBinding otherwise(final ObservableBooleanValue otherwiseValue) { 397 return super.otherwise(otherwiseValue); 398 } 399 400 /** 401 * Defines a constant value of the ternary expression that is returned 402 * if the condition is {@code false}. 403 * 404 * @param otherwiseValue 405 * the value 406 * @return the complete {@link BooleanBinding} 407 */ 408 public BooleanBinding otherwise(final boolean otherwiseValue) { 409 return super.otherwise(otherwiseValue); 410 } 411 } 412 413 /** 414 * An intermediate class needed while assembling the ternary expression. It 415 * should not be used in another context. 416 * @since JavaFX 2.0 417 */ 418 public class BooleanConditionBuilder { 419 420 private ObservableValue<Boolean> thenValue; 421 422 private BooleanConditionBuilder(final ObservableBooleanValue thenValue) { 423 this.thenValue = thenValue; 424 } 425 426 /** 427 * Defines the {@link javafx.beans.value.ObservableBooleanValue} whose 428 * value is returned by the ternary expression if the condition is 429 * {@code false}. 430 * 431 * @param otherwiseValue 432 * the value 433 * @return the complete {@link BooleanBinding} 434 */ 435 public BooleanBinding otherwise(final ObservableBooleanValue otherwiseValue) { 436 Objects.requireNonNull(otherwiseValue, VALUE_MUST_BE_SPECIFIED); 437 return new BooleanConditionHolder(thenValue, otherwiseValue).getBinding(); 438 } 439 440 /** 441 * Defines a constant value of the ternary expression that is returned 442 * if the condition is {@code false}. 443 * 444 * @param otherwiseValue 445 * the value 446 * @return the complete {@link BooleanBinding} 447 */ 448 public BooleanBinding otherwise(final boolean otherwiseValue) { 449 return otherwise(BooleanConstant.valueOf(otherwiseValue)); 450 } 451 } 452 453 /** 454 * Defines the {@link javafx.beans.value.ObservableBooleanValue} which value 455 * is returned by the ternary expression if the condition is {@code true}. 456 * 457 * @param thenValue 458 * the value 459 * @return the intermediate result which still requires the otherwise-branch 460 */ 461 public BooleanConditionBuilder then(final ObservableBooleanValue thenValue) { 462 Objects.requireNonNull(thenValue, VALUE_MUST_BE_SPECIFIED); 463 return new BooleanConditionBuilder(thenValue); 464 } 465 466 /** 467 * Defines a constant value of the ternary expression that is returned if 468 * the condition is {@code true}. 469 * 470 * @param thenValue 471 * the value 472 * @return the intermediate result which still requires the otherwise-branch 473 */ 474 public BooleanConditionBuilder then(final boolean thenValue) { 475 return then(BooleanConstant.valueOf(thenValue)); 476 } 477 478 private class StringConditionHolder extends ConditionHolder<String, StringBinding> { 479 480 protected StringConditionHolder(ObservableStringValue then, ObservableStringValue otherwise) { 481 binding = new StringBinding() { 482 483 @Override 484 protected String computeValue() { 485 return StringConditionHolder.super.computeValue(); 486 } 487 488 @Override 489 public void dispose() { 490 StringConditionHolder.super.dispose(); 491 } 492 493 @Override 494 public ObservableList<ObservableValue<?>> getDependencies() { 495 return StringConditionHolder.super.getDependencies(); 496 } 497 }; 498 registerListeners(then, otherwise); 499 } 500 } 501 502 /** 503 * An intermediate class needed while assembling the ternary expression. It 504 * should not be used in another context. 505 * @since JavaFX 2.0 506 */ 507 public class StringConditionBuilder { 508 509 private ObservableStringValue thenValue; 510 511 private StringConditionBuilder(final ObservableStringValue thenValue) { 512 this.thenValue = thenValue; 513 } 514 515 /** 516 * Defines the {@link javafx.beans.value.ObservableStringValue} whose 517 * value is returned by the ternary expression if the condition is 518 * {@code false}. 519 * 520 * @param otherwiseValue 521 * the value 522 * @return the complete {@link StringBinding} 523 */ 524 public StringBinding otherwise(final ObservableStringValue otherwiseValue) { 525 Objects.requireNonNull(otherwiseValue, VALUE_MUST_BE_SPECIFIED); 526 return new StringConditionHolder(thenValue, otherwiseValue).getBinding(); 527 } 528 529 /** 530 * Defines a constant value of the ternary expression that is returned 531 * if the condition is {@code false}. 532 * 533 * @param otherwiseValue 534 * the value 535 * @return the complete {@link StringBinding} 536 */ 537 public StringBinding otherwise(final String otherwiseValue) { 538 return otherwise(StringConstant.valueOf(otherwiseValue)); 539 } 540 } 541 542 /** 543 * Defines the {@link javafx.beans.value.ObservableStringValue} which value 544 * is returned by the ternary expression if the condition is {@code true}. 545 * 546 * @param thenValue 547 * the value 548 * @return the intermediate result which still requires the otherwise-branch 549 */ 550 public StringConditionBuilder then(final ObservableStringValue thenValue) { 551 Objects.requireNonNull(thenValue, VALUE_MUST_BE_SPECIFIED); 552 return new StringConditionBuilder(thenValue); 553 } 554 555 /** 556 * Defines a constant value of the ternary expression that is returned if 557 * the condition is {@code true}. 558 * 559 * @param thenValue 560 * the value 561 * @return the intermediate result which still requires the otherwise-branch 562 */ 563 public StringConditionBuilder then(final String thenValue) { 564 return then(StringConstant.valueOf(thenValue)); 565 } 566 567 private class ObjectConditionHolder<T> extends ConditionHolder<T, ObjectBinding<T>> { 568 569 protected ObjectConditionHolder(ObservableObjectValue<T> then, ObservableObjectValue<T> otherwise) { 570 binding = new ObjectBinding<T>() { 571 572 @Override 573 protected T computeValue() { 574 return ObjectConditionHolder.super.computeValue(); 575 } 576 577 @Override 578 public void dispose() { 579 ObjectConditionHolder.super.dispose(); 580 } 581 582 @Override 583 public ObservableList<ObservableValue<?>> getDependencies() { 584 return ObjectConditionHolder.super.getDependencies(); 585 } 586 }; 587 registerListeners(then, otherwise); 588 } 589 } 590 591 /** 592 * An intermediate class needed while assembling the ternary expression. It 593 * should not be used in another context. 594 * @since JavaFX 2.0 595 */ 596 public class ObjectConditionBuilder<T> { 597 598 private ObservableObjectValue<T> thenValue; 599 600 private ObjectConditionBuilder(final ObservableObjectValue<T> thenValue) { 601 this.thenValue = thenValue; 602 } 603 604 /** 605 * Defines the {@link javafx.beans.value.ObservableObjectValue} which 606 * value is returned by the ternary expression if the condition is 607 * {@code false}. 608 * 609 * @param otherwiseValue 610 * the value 611 * @return the complete {@link ObjectBinding} 612 */ 613 public ObjectBinding<T> otherwise(final ObservableObjectValue<T> otherwiseValue) { 614 Objects.requireNonNull(otherwiseValue, VALUE_MUST_BE_SPECIFIED); 615 return new ObjectConditionHolder<>(thenValue, otherwiseValue).getBinding(); 616 } 617 618 /** 619 * Defines a constant value of the ternary expression that is returned 620 * if the condition is {@code false}. 621 * 622 * @param otherwiseValue 623 * the value 624 * @return the complete {@link ObjectBinding} 625 */ 626 public ObjectBinding<T> otherwise(final T otherwiseValue) { 627 return otherwise(ObjectConstant.valueOf(otherwiseValue)); 628 } 629 } 630 631 /** 632 * Defines the {@link javafx.beans.value.ObservableObjectValue} which value 633 * is returned by the ternary expression if the condition is {@code true}. 634 * 635 * @param <T> the type of the intermediate result 636 * @param thenValue 637 * the value 638 * @return the intermediate result which still requires the otherwise-branch 639 */ 640 public <T> ObjectConditionBuilder<T> then(final ObservableObjectValue<T> thenValue) { 641 Objects.requireNonNull(thenValue, VALUE_MUST_BE_SPECIFIED); 642 return new ObjectConditionBuilder<T>(thenValue); 643 } 644 645 /** 646 * Defines a constant value of the ternary expression that is returned if 647 * the condition is {@code true}. 648 * 649 * @param <T> the type of the intermediate result 650 * @param thenValue 651 * the value 652 * @return the intermediate result which still requires the otherwise-branch 653 */ 654 public <T> ObjectConditionBuilder<T> then(final T thenValue) { 655 return then(ObjectConstant.valueOf(thenValue)); 656 } 657 658 }