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 }