001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.assertions; 014 015import static org.apache.juneau.common.internal.ArgUtils.*; 016import static org.apache.juneau.common.internal.StringUtils.*; 017import static org.apache.juneau.common.internal.ThrowableUtils.*; 018import static org.apache.juneau.internal.CollectionUtils.*; 019 020import java.io.*; 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.cp.*; 025import org.apache.juneau.internal.*; 026import org.apache.juneau.json.*; 027import org.apache.juneau.reflect.*; 028import org.apache.juneau.serializer.*; 029 030/** 031 * Used for fluent assertion calls against POJOs. 032 * 033 * <h5 class='section'>Test Methods:</h5> 034 * <p> 035 * <ul class='javatree'> 036 * <li class='jc'>{@link FluentObjectAssertion} 037 * <ul class='javatreec'> 038 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 039 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 040 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 041 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 042 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 043 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 044 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 045 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 046 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 047 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 048 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 049 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 050 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 051 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 052 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 053 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 054 * </ul> 055 * </ul> 056 * 057 * <h5 class='section'>Transform Methods:</h5> 058 * <p> 059 * <ul class='javatree'> 060 * <li class='jc'>{@link FluentObjectAssertion} 061 * <ul class='javatreec'> 062 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 063 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 064 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 065 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 066 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 067 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 068 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 069 * </ul> 070 * </ul> 071 * 072 * <h5 class='section'>Configuration Methods:</h5> 073 * <p> 074 * <ul class='javatree'> 075 * <li class='jc'>{@link Assertion} 076 * <ul class='javatreec'> 077 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 078 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 079 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 080 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 081 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 082 * </ul> 083 * </ul> 084 * 085 * <h5 class='section'>See Also:</h5><ul> 086 * <li class='link'><a class="doclink" href="../../../../index.html#ja.Overview">Overview > juneau-assertions > Overview</a> 087 * </ul> 088 * 089 * @param <T> The object type. 090 * @param <R> The return type. 091 */ 092@FluentSetters(returns="FluentObjectAssertion<T,R>") 093public class FluentObjectAssertion<T,R> extends FluentAssertion<R> { 094 095 //----------------------------------------------------------------------------------------------------------------- 096 // Static 097 //----------------------------------------------------------------------------------------------------------------- 098 099 private static final Messages MESSAGES = Messages.of(FluentObjectAssertion.class, "Messages"); 100 private static final String 101 MSG_unexpectedType = MESSAGES.getString("unexpectedType"), 102 MSG_unexpectedComparison = MESSAGES.getString("unexpectedComparison"), 103 MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"), 104 MSG_unexpectedValueDidNotExpect = MESSAGES.getString("unexpectedValueDidNotExpect"), 105 MSG_notTheSameValue = MESSAGES.getString("notTheSameValue"), 106 MSG_valueWasNull = MESSAGES.getString("valueWasNull"), 107 MSG_valueWasNotNull = MESSAGES.getString("valueWasNotNull"), 108 MSG_expectedValueNotFound = MESSAGES.getString("expectedValueNotFound"), 109 MSG_unexpectedValueFound = MESSAGES.getString("unexpectedValueFound"), 110 MSG_unexpectedValue2 = MESSAGES.getString("unexpectedValue2"); 111 112 private static JsonSerializer JSON = JsonSerializer.create() 113 .json5() 114 .build(); 115 116 private static JsonSerializer JSON_SORTED = JsonSerializer.create() 117 .json5() 118 .sortProperties() 119 .sortCollections() 120 .sortMaps() 121 .build(); 122 123 //----------------------------------------------------------------------------------------------------------------- 124 // Instance 125 //----------------------------------------------------------------------------------------------------------------- 126 127 private final T value; 128 129 /** 130 * Constructor. 131 * 132 * @param value 133 * The object being tested. 134 * <br>Can be <jk>null</jk>. 135 * @param returns 136 * The object to return after a test method is called. 137 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 138 * used on the same assertion. 139 */ 140 public FluentObjectAssertion(T value, R returns) { 141 this(null, value, returns); 142 } 143 144 /** 145 * Chained constructor. 146 * 147 * <p> 148 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 149 * 150 * @param creator 151 * The assertion that created this assertion. 152 * <br>Should be <jk>null</jk> if this is the top-level assertion. 153 * @param value 154 * The object being tested. 155 * <br>Can be <jk>null</jk>. 156 * @param returns 157 * The object to return after a test method is called. 158 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 159 * used on the same assertion. 160 */ 161 public FluentObjectAssertion(Assertion creator, T value, R returns) { 162 super(creator, returns); 163 this.value = value; 164 } 165 166 //----------------------------------------------------------------------------------------------------------------- 167 // Transform methods 168 //----------------------------------------------------------------------------------------------------------------- 169 170 /** 171 * Converts this object to a string using {@link Object#toString} and returns it as a new assertion. 172 * 173 * <h5 class='section'>Example:</h5> 174 * <p class='bjava'> 175 * <jc>// Validates that the specified object is "foobar" after converting to a string.</jc> 176 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 177 * .asString() 178 * .is(<js>"foobar"</js>); 179 * </p> 180 * 181 * @return A new fluent string assertion. 182 */ 183 public FluentStringAssertion<R> asString() { 184 return new FluentStringAssertion<>(this, valueAsString(), returns()); 185 } 186 187 /** 188 * Converts this object to text using the specified serializer and returns it as a new assertion. 189 * 190 * <h5 class='section'>Example:</h5> 191 * <p class='bjava'> 192 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 193 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 194 * .asString(XmlSerializer.<jsf>DEFAULT</jsf>) 195 * .is(<js>"<object><foo>bar</foo><baz>qux</baz></object>"</js>); 196 * </p> 197 * 198 * @param ws The serializer to use to convert the object to text. 199 * @return A new fluent string assertion. 200 */ 201 public FluentStringAssertion<R> asString(WriterSerializer ws) { 202 try { 203 return new FluentStringAssertion<>(this, ws.serialize(value), returns()); 204 } catch (SerializeException e) { 205 throw asRuntimeException(e); 206 } 207 } 208 209 /** 210 * Converts this object to a string using the specified function and returns it as a new assertion. 211 * 212 * <h5 class='section'>Example:</h5> 213 * <p class='bjava'> 214 * <jc>// Validates that the specified object is "foobar" after converting to a string.</jc> 215 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 216 * .asString(<jv>x</jv>-><jv>x</jv>.toString()) 217 * .is(<js>"foobar"</js>); 218 * </p> 219 * 220 * @param function The conversion function. 221 * @return A new fluent string assertion. 222 */ 223 public FluentStringAssertion<R> asString(Function<T,String> function) { 224 return new FluentStringAssertion<>(this, function.apply(value), returns()); 225 } 226 227 /** 228 * Converts this object to simplified JSON and returns it as a new assertion. 229 * 230 * <h5 class='section'>Example:</h5> 231 * <p class='bjava'> 232 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 233 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 234 * .asJson() 235 * .is(<js>"{foo:'bar',baz:'qux'}"</js>); 236 * </p> 237 * 238 * @return A new fluent string assertion. 239 */ 240 public FluentStringAssertion<R> asJson() { 241 return asString(JSON); 242 } 243 244 /** 245 * Converts this object to sorted simplified JSON and returns it as a new assertion. 246 * 247 * <h5 class='section'>Example:</h5> 248 * <p class='bjava'> 249 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 250 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 251 * .asJsonSorted() 252 * .is(<js>"{baz:'qux',foo:'bar'}"</js>); 253 * </p> 254 * 255 * @return A new fluent string assertion. 256 */ 257 public FluentStringAssertion<R> asJsonSorted() { 258 return asString(JSON_SORTED); 259 } 260 261 /** 262 * Applies a transform on the inner object and returns a new inner object. 263 * 264 * @param function The function to apply. 265 * @return This object. 266 */ 267 public FluentObjectAssertion<T,R> asTransformed(Function<T,T> function) { 268 return new FluentObjectAssertion<>(this, function.apply(orElse(null)), returns()); 269 } 270 271 /** 272 * Applies a transform on the inner object and returns a new inner object. 273 * 274 * @param <T2> The transform-to type. 275 * @param function The function to apply. 276 * @return This object. 277 */ 278 public <T2> FluentObjectAssertion<T2,R> asTransformedTo(Function<T,T2> function) { 279 return new FluentObjectAssertion<>(this, function.apply(orElse(null)), returns()); 280 } 281 282 /** 283 * Converts this assertion into an {@link FluentAnyAssertion} so that it can be converted to other assertion types. 284 * 285 * @return This object. 286 */ 287 public FluentAnyAssertion<T,R> asAny() { 288 return new FluentAnyAssertion<>(this, orElse(null), returns()); 289 } 290 291 //----------------------------------------------------------------------------------------------------------------- 292 // Test methods 293 //----------------------------------------------------------------------------------------------------------------- 294 295 /** 296 * Asserts that the object is not null. 297 * 298 * <p> 299 * Equivalent to {@link #isNotNull()}. 300 * 301 * @return The fluent return object. 302 * @throws AssertionError If assertion failed. 303 */ 304 public R isExists() throws AssertionError { 305 return isNotNull(); 306 } 307 308 /** 309 * Asserts that the object i null. 310 * 311 * <p> 312 * Equivalent to {@link #isNotNull()}. 313 * 314 * @return The fluent return object. 315 * @throws AssertionError If assertion failed. 316 */ 317 public R isNull() throws AssertionError { 318 if (value != null) 319 throw error(MSG_valueWasNotNull); 320 return returns(); 321 } 322 323 /** 324 * Asserts that the object is not null. 325 * 326 * <p> 327 * Equivalent to {@link #isNotNull()}. 328 * 329 * @return The fluent return object. 330 * @throws AssertionError If assertion failed. 331 */ 332 public R isNotNull() throws AssertionError { 333 if (value == null) 334 throw error(MSG_valueWasNull); 335 return returns(); 336 } 337 338 /** 339 * Asserts that the value equals the specified value. 340 * 341 * @param value The value to check against. 342 * @return The fluent return object. 343 * @throws AssertionError If assertion failed. 344 */ 345 public R is(T value) throws AssertionError { 346 if (this.value == value) 347 return returns(); 348 if (! equals(orElse(null), value)) 349 throw error(MSG_unexpectedValue, value, this.value); 350 return returns(); 351 } 352 353 /** 354 * Asserts that the value converted to a string equals the specified value. 355 * 356 * @param value The value to check against. 357 * @return The fluent return object. 358 * @throws AssertionError If assertion failed. 359 */ 360 public R isString(String value) { 361 return asString().is(value); 362 } 363 364 /** 365 * Asserts that the value does not equal the specified value. 366 * 367 * @param value The value to check against. 368 * @return The fluent return object. 369 * @throws AssertionError If assertion failed. 370 */ 371 public R isNot(T value) throws AssertionError { 372 if (equals(orElse(null), value)) 373 throw error(MSG_unexpectedValueDidNotExpect, value, orElse(null)); 374 return returns(); 375 } 376 377 /** 378 * Asserts that the value is one of the specified values. 379 * 380 * @param values The values to check against. 381 * @return The fluent return object. 382 * @throws AssertionError If assertion failed. 383 */ 384 @SuppressWarnings("unchecked") 385 public R isAny(T...values) throws AssertionError { 386 for (T v : values) 387 if (equals(orElse(null), v)) 388 return returns(); 389 throw error(MSG_expectedValueNotFound, values, value); 390 } 391 392 /** 393 * Asserts that the value is not one of the specified values. 394 * 395 * @param values The values to check against. 396 * @return The fluent return object. 397 * @throws AssertionError If assertion failed. 398 */ 399 @SuppressWarnings("unchecked") 400 public R isNotAny(T...values) throws AssertionError { 401 for (T v : values) 402 if (equals(orElse(null), v)) 403 throw error(MSG_unexpectedValueFound, v, value); 404 return returns(); 405 } 406 407 /** 408 * Asserts that the specified object is the same object as this object. 409 * 410 * @param value The value to check against. 411 * @return The fluent return object. 412 * @throws AssertionError If assertion failed. 413 */ 414 public R isSame(T value) throws AssertionError { 415 if (this.value == value) 416 return returns(); 417 throw error(MSG_notTheSameValue, value, ObjectUtils.identity(value), this.value, ObjectUtils.identity(this.value)); 418 } 419 420 /** 421 * Verifies that two objects are equivalent after converting them both to JSON. 422 * 423 * @param o The object to compare against. 424 * @return The fluent return object. 425 * @throws AssertionError If assertion failed. 426 */ 427 public R isSameJsonAs(Object o) throws AssertionError { 428 return isSameSerializedAs(o, JSON); 429 } 430 431 /** 432 * Verifies that two objects are equivalent after converting them both to sorted JSON. 433 * 434 * <p> 435 * Properties, maps, and collections are all sorted on both objects before comparison. 436 * 437 * @param o The object to compare against. 438 * @return The fluent return object. 439 * @throws AssertionError If assertion failed. 440 */ 441 public R isSameSortedJsonAs(Object o) { 442 return isSameSerializedAs(o, JSON_SORTED); 443 } 444 445 /** 446 * Asserts that the specified object is the same as this object after converting both to strings using the specified serializer. 447 * 448 * @param o The object to compare against. 449 * @param serializer The serializer to use to serialize this object. 450 * @return The fluent return object. 451 * @throws AssertionError If assertion failed. 452 */ 453 public R isSameSerializedAs(Object o, WriterSerializer serializer) { 454 String s1 = serializer.toString(value); 455 String s2 = serializer.toString(o); 456 if (ne(s1, s2)) 457 throw error(MSG_unexpectedComparison, s2, s1); 458 return returns(); 459 } 460 461 /** 462 * Asserts that the object is an instance of the specified class. 463 * 464 * <h5 class='section'>Example:</h5> 465 * <p class='bjava'> 466 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 467 * <jsm>assertObject</jsm>(<jv>myPojo</jv>).isType(MyBean.<jk>class</jk>); 468 * </p> 469 * 470 * @param parent The value to check against. 471 * @return The fluent return object. 472 * @throws AssertionError If assertion failed. 473 */ 474 public R isType(Class<?> parent) throws AssertionError { 475 assertArgNotNull("parent", parent); 476 if (! ClassInfo.of(value()).isChildOf(parent)) 477 throw error(MSG_unexpectedType, className(parent), className(value)); 478 return returns(); 479 } 480 481 /** 482 * Asserts that the object is an instance of the specified class. 483 * 484 * <h5 class='section'>Example:</h5> 485 * <p class='bjava'> 486 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 487 * <jsm>assertObject</jsm>(<jv>myPojo</jv>).isExactType(MyBean.<jk>class</jk>); 488 * </p> 489 * 490 * @param type The value to check against. 491 * @return The fluent return object. 492 * @throws AssertionError If assertion failed. 493 */ 494 public R isExactType(Class<?> type) throws AssertionError { 495 assertArgNotNull("parent", type); 496 if (value().getClass() != type) 497 throw error(MSG_unexpectedType, className(type), className(value)); 498 return returns(); 499 } 500 501 /** 502 * Asserts that the value passes the specified predicate test. 503 * 504 * @param test The predicate to use to test the value. 505 * @return The fluent return object. 506 * @throws AssertionError If assertion failed. 507 */ 508 public R is(Predicate<T> test) throws AssertionError { 509 if (test != null && ! test.test(value)) 510 throw error(getFailureMessage(test, value)); 511 return returns(); 512 } 513 514 /** 515 * Converts this object to simplified JSON and runs the {@link FluentStringAssertion#is(String)} on the result. 516 * 517 * <h5 class='section'>Example:</h5> 518 * <p class='bjava'> 519 * <jc>// Validates that the specified object is an instance of MyBean.</jc> 520 * <jsm>assertObject</jsm>(<jv>myPojo</jv>) 521 * .asJson() 522 * .is(<js>"{foo:'bar',baz:'qux'}"</js>); 523 * </p> 524 * 525 * @param value The expected string value. 526 * @return The fluent return object. 527 */ 528 public R isJson(String value) { 529 return asJson().is(value); 530 } 531 532 //----------------------------------------------------------------------------------------------------------------- 533 // Fluent setters 534 //----------------------------------------------------------------------------------------------------------------- 535 536 // <FluentSetters> 537 538 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 539 public FluentObjectAssertion<T,R> setMsg(String msg, Object...args) { 540 super.setMsg(msg, args); 541 return this; 542 } 543 544 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 545 public FluentObjectAssertion<T,R> setOut(PrintStream value) { 546 super.setOut(value); 547 return this; 548 } 549 550 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 551 public FluentObjectAssertion<T,R> setSilent() { 552 super.setSilent(); 553 return this; 554 } 555 556 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 557 public FluentObjectAssertion<T,R> setStdOut() { 558 super.setStdOut(); 559 return this; 560 } 561 562 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 563 public FluentObjectAssertion<T,R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 564 super.setThrowable(value); 565 return this; 566 } 567 568 // </FluentSetters> 569 570 //----------------------------------------------------------------------------------------------------------------- 571 // Utility methods 572 //----------------------------------------------------------------------------------------------------------------- 573 574 /** 575 * Returns the inner value after asserting it is not <jk>null</jk>. 576 * 577 * @return The inner value. 578 * @throws AssertionError If inner value was <jk>null</jk>. 579 */ 580 protected T value() throws AssertionError { 581 isExists(); 582 return value; 583 } 584 585 /** 586 * Returns the inner value as a string. 587 * 588 * @return The inner value as a string, or <jk>null</jk> if the value was null. 589 */ 590 protected String valueAsString() { 591 return stringify(value); 592 } 593 594 /** 595 * Returns the inner value or the other value if the value is <jk>null</jk>. 596 * 597 * @param other The other value. 598 * @return The inner value. 599 */ 600 protected T orElse(T other) { 601 return value == null ? other : value; 602 } 603 604 /** 605 * Returns <jk>true</jk> if the inner value is null. 606 * 607 * @return <jk>true</jk> if the inner value is null. 608 */ 609 protected boolean valueIsNull() { 610 return value == null; 611 } 612 613 /** 614 * Returns <jk>true</jk> if the inner value is not null. 615 * 616 * @return <jk>true</jk> if the inner value is not null. 617 */ 618 protected boolean valueIsNotNull() { 619 return value != null; 620 } 621 622 /** 623 * Returns the value wrapped in an {@link Optional}. 624 * 625 * @return The value wrapped in an {@link Optional}. 626 */ 627 protected Optional<T> opt() { 628 return optional(value); 629 } 630 631 /** 632 * Returns the result of running the specified function against the value and returns the result. 633 * 634 * @param <T2> The mapper-to type. 635 * @param mapper The function to run against the value. 636 * @return The result, never <jk>null</jk>. 637 */ 638 protected <T2> Optional<T2> map(Function<? super T, ? extends T2> mapper) { 639 return opt().map(mapper); 640 } 641 642 /** 643 * Returns the predicate failure message. 644 * 645 * <p> 646 * If the predicate extends from {@link AssertionPredicate}, then the message comes from {@link AssertionPredicate#getFailureMessage()}. 647 * Otherwise, returns a generic <js>"Unexpected value: x"</js> message. 648 * 649 * @param p The function to run against the value. 650 * @param value The value that failed the test. 651 * @return The result, never <jk>null</jk>. 652 */ 653 protected String getFailureMessage(Predicate<?> p, Object value) { 654 if (p instanceof AssertionPredicate) 655 return ((AssertionPredicate<?>)p).getFailureMessage(); 656 return format(MSG_unexpectedValue2, value); 657 } 658 659 /** 660 * Checks two objects for equality. 661 * 662 * @param o1 The first object. 663 * @param o2 The second object. 664 * @return <jk>true</jk> if the objects are equal. 665 */ 666 protected boolean equals(Object o1, Object o2) { 667 if (o1 == o2) 668 return true; 669 if (o1 == null || o2 == null) 670 return false; 671 if (o1.equals(o2)) 672 return true; 673 if (o1.getClass().isArray()) 674 return stringifyDeep(o1).equals(stringifyDeep(o2)); 675 return false; 676 } 677 678 /** 679 * Returns the string form of the inner object. 680 * Subclasses can override this method to affect the {@link #asString()} method (and related). 681 */ 682 @Override /* Object */ 683 public String toString() { 684 return valueAsString(); 685 } 686}