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.*; 017 018import java.io.*; 019import java.util.*; 020import java.util.function.*; 021import java.util.regex.*; 022 023import org.apache.juneau.common.internal.*; 024import org.apache.juneau.cp.*; 025import org.apache.juneau.internal.*; 026import org.apache.juneau.serializer.*; 027 028/** 029 * Used for fluent assertion calls against strings. 030 * 031 * <h5 class='section'>Example:</h5> 032 * <p class='bjava'> 033 * <jc>// Validates the response body of an HTTP call is the text "OK".</jc> 034 * <jv>client</jv> 035 * .get(<jsf>URL</jsf>) 036 * .run() 037 * .assertContent().is(<js>"OK"</js>); 038 * </p> 039 * 040 * 041 * <h5 class='section'>Test Methods:</h5> 042 * <p> 043 * <ul class='javatree'> 044 * <li class='jc'>{@link FluentStringAssertion} 045 * <ul class='javatreec'> 046 * <li class='jm'>{@link FluentStringAssertion#is(String) is(String)} 047 * <li class='jm'>{@link FluentStringAssertion#isNot(String) isNot(String)} 048 * <li class='jm'>{@link FluentStringAssertion#isLines(String...) isLines(String...)} 049 * <li class='jm'>{@link FluentStringAssertion#isSortedLines(String...) isSortedLines(String...)} 050 * <li class='jm'>{@link FluentStringAssertion#isIc(String) isIc(String)} 051 * <li class='jm'>{@link FluentStringAssertion#isNotIc(String) isNotIc(String)} 052 * <li class='jm'>{@link FluentStringAssertion#isContains(String...) isContains(String...)} 053 * <li class='jm'>{@link FluentStringAssertion#isNotContains(String...) isNotContains(String...)} 054 * <li class='jm'>{@link FluentStringAssertion#isEmpty() isEmpty()} 055 * <li class='jm'>{@link FluentStringAssertion#isNotEmpty() isNotEmpty()} 056 * <li class='jm'>{@link FluentStringAssertion#isString(Object) isString(Object)} 057 * <li class='jm'>{@link FluentStringAssertion#isMatches(String) isMatches(String)} 058 * <li class='jm'>{@link FluentStringAssertion#isPattern(String) isPattern(String)} 059 * <li class='jm'>{@link FluentStringAssertion#isPattern(String,int) isPattern(String,int)} 060 * <li class='jm'>{@link FluentStringAssertion#isPattern(Pattern) isPattern(Pattern)} 061 * <li class='jm'>{@link FluentStringAssertion#isStartsWith(String) isStartsWith(String)} 062 * <li class='jm'>{@link FluentStringAssertion#isEndsWith(String) isEndsWith(String)} 063 * </ul> 064 * <li class='jc'>{@link FluentObjectAssertion} 065 * <ul class='javatreec'> 066 * <li class='jm'>{@link FluentObjectAssertion#isExists() isExists()} 067 * <li class='jm'>{@link FluentObjectAssertion#is(Object) is(Object)} 068 * <li class='jm'>{@link FluentObjectAssertion#is(Predicate) is(Predicate)} 069 * <li class='jm'>{@link FluentObjectAssertion#isNot(Object) isNot(Object)} 070 * <li class='jm'>{@link FluentObjectAssertion#isAny(Object...) isAny(Object...)} 071 * <li class='jm'>{@link FluentObjectAssertion#isNotAny(Object...) isNotAny(Object...)} 072 * <li class='jm'>{@link FluentObjectAssertion#isNull() isNull()} 073 * <li class='jm'>{@link FluentObjectAssertion#isNotNull() isNotNull()} 074 * <li class='jm'>{@link FluentObjectAssertion#isString(String) isString(String)} 075 * <li class='jm'>{@link FluentObjectAssertion#isJson(String) isJson(String)} 076 * <li class='jm'>{@link FluentObjectAssertion#isSame(Object) isSame(Object)} 077 * <li class='jm'>{@link FluentObjectAssertion#isSameJsonAs(Object) isSameJsonAs(Object)} 078 * <li class='jm'>{@link FluentObjectAssertion#isSameSortedJsonAs(Object) isSameSortedJsonAs(Object)} 079 * <li class='jm'>{@link FluentObjectAssertion#isSameSerializedAs(Object, WriterSerializer) isSameSerializedAs(Object, WriterSerializer)} 080 * <li class='jm'>{@link FluentObjectAssertion#isType(Class) isType(Class)} 081 * <li class='jm'>{@link FluentObjectAssertion#isExactType(Class) isExactType(Class)} 082 * </ul> 083 * </ul> 084 * 085 * <h5 class='section'>Transform Methods:</h5> 086 * <p> 087 * <ul class='javatree'> 088 * <li class='jc'>{@link FluentStringAssertion} 089 * <ul class='javatreec'> 090 * <li class='jm'>{@link FluentStringAssertion#asReplaceAll(String,String) asReplaceAll(String,String)} 091 * <li class='jm'>{@link FluentStringAssertion#asReplace(String,String) asReplace(String,String)} 092 * <li class='jm'>{@link FluentStringAssertion#asUrlDecode() asUrlDecode()} 093 * <li class='jm'>{@link FluentStringAssertion#asLc() asLc()} 094 * <li class='jm'>{@link FluentStringAssertion#asUc() asUc()} 095 * <li class='jm'>{@link FluentStringAssertion#asLines() asLines()} 096 * <li class='jm'>{@link FluentStringAssertion#asSplit(String) asSplit(String)} 097 * <li class='jm'>{@link FluentStringAssertion#asLength() asLength()} 098 * <li class='jm'>{@link FluentStringAssertion#asOneLine() asOneLine()} 099 * </ul> 100 * <li class='jc'>{@link FluentObjectAssertion} 101 * <ul class='javatreec'> 102 * <li class='jm'>{@link FluentObjectAssertion#asString() asString()} 103 * <li class='jm'>{@link FluentObjectAssertion#asString(WriterSerializer) asString(WriterSerializer)} 104 * <li class='jm'>{@link FluentObjectAssertion#asString(Function) asString(Function)} 105 * <li class='jm'>{@link FluentObjectAssertion#asJson() asJson()} 106 * <li class='jm'>{@link FluentObjectAssertion#asJsonSorted() asJsonSorted()} 107 * <li class='jm'>{@link FluentObjectAssertion#asTransformed(Function) asApplied(Function)} 108 * <li class='jm'>{@link FluentObjectAssertion#asAny() asAny()} 109 * </ul> 110 * </ul> 111 * 112 * <h5 class='section'>Configuration Methods:</h5> 113 * <p> 114 * <ul class='javatree'> 115 * <li class='jc'>{@link Assertion} 116 * <ul class='javatreec'> 117 * <li class='jm'>{@link Assertion#setMsg(String, Object...) setMsg(String, Object...)} 118 * <li class='jm'>{@link Assertion#setOut(PrintStream) setOut(PrintStream)} 119 * <li class='jm'>{@link Assertion#setSilent() setSilent()} 120 * <li class='jm'>{@link Assertion#setStdOut() setStdOut()} 121 * <li class='jm'>{@link Assertion#setThrowable(Class) setThrowable(Class)} 122 * </ul> 123 * </ul> 124 * 125 * <h5 class='section'>See Also:</h5><ul> 126 * <li class='link'><a class="doclink" href="../../../../index.html#ja.Overview">Overview > juneau-assertions > Overview</a> 127 * </ul> 128 * 129 * @param <R> The return type. 130 */ 131@FluentSetters(returns="FluentStringAssertion<R>") 132public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> { 133 134 //----------------------------------------------------------------------------------------------------------------- 135 // Static 136 //----------------------------------------------------------------------------------------------------------------- 137 138 private static final Messages MESSAGES = Messages.of(FluentStringAssertion.class, "Messages"); 139 private static final String 140 MSG_stringDifferedAtPosition = MESSAGES.getString("stringDifferedAtPosition"), 141 MSG_expectedStringHadDifferentNumbersOfLines = MESSAGES.getString("expectedStringHadDifferentNumbersOfLines"), 142 MSG_expectedStringHadDifferentValuesAtLine = MESSAGES.getString("expectedStringHadDifferentValuesAtLine"), 143 MSG_stringEqualedUnexpected = MESSAGES.getString("stringEqualedUnexpected"), 144 MSG_stringDidNotContainExpectedSubstring = MESSAGES.getString("stringDidNotContainExpectedSubstring"), 145 MSG_stringContainedUnexpectedSubstring = MESSAGES.getString("stringContainedUnexpectedSubstring"), 146 MSG_stringWasNotEmpty = MESSAGES.getString("stringWasNotEmpty"), 147 MSG_stringWasNull = MESSAGES.getString("stringWasNull"), 148 MSG_stringWasEmpty = MESSAGES.getString("stringWasEmpty"), 149 MSG_stringDidNotMatchExpectedPattern = MESSAGES.getString("stringDidNotMatchExpectedPattern"), 150 MSG_stringDidNotStartWithExpected = MESSAGES.getString("stringDidNotStartWithExpected"), 151 MSG_stringDidNotEndWithExpected = MESSAGES.getString("stringDidNotEndWithExpected"); 152 153 //----------------------------------------------------------------------------------------------------------------- 154 // Instance 155 //----------------------------------------------------------------------------------------------------------------- 156 157 private boolean javaStrings; 158 159 /** 160 * Constructor. 161 * 162 * @param value 163 * The object being tested. 164 * <br>Can be <jk>null</jk>. 165 * @param returns 166 * The object to return after a test method is called. 167 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 168 * used on the same assertion. 169 */ 170 public FluentStringAssertion(String value, R returns) { 171 this(null, value, returns); 172 } 173 174 /** 175 * Chained constructor. 176 * 177 * <p> 178 * Used when transforming one assertion into another so that the assertion config can be used by the new assertion. 179 * 180 * @param creator 181 * The assertion that created this assertion. 182 * <br>Should be <jk>null</jk> if this is the top-level assertion. 183 * @param value 184 * The object being tested. 185 * <br>Can be <jk>null</jk>. 186 * @param returns 187 * The object to return after a test method is called. 188 * <br>If <jk>null</jk>, the test method returns this object allowing multiple test method calls to be 189 * used on the same assertion. 190 */ 191 public FluentStringAssertion(Assertion creator, String value, R returns) { 192 super(creator, value, returns); 193 } 194 195 //----------------------------------------------------------------------------------------------------------------- 196 // Config methods 197 //----------------------------------------------------------------------------------------------------------------- 198 199 /** 200 * When enabled, text in the message is converted to valid Java strings. 201 * 202 * <p class='bjava'> 203 * <jv>value</jv>.replaceAll(<js>"\\\\"</js>, <js>"\\\\\\\\"</js>).replaceAll(<js>"\n"</js>, <js>"\\\\n"</js>).replaceAll(<js>"\t"</js>, <js>"\\\\t"</js>); 204 * </p> 205 * 206 * @return This object. 207 */ 208 @FluentSetter 209 public FluentStringAssertion<R> asJavaStrings() { 210 this.javaStrings = true; 211 return this; 212 } 213 214 //----------------------------------------------------------------------------------------------------------------- 215 // Transform methods 216 //----------------------------------------------------------------------------------------------------------------- 217 218 @Override /* FluentObjectAssertion */ 219 public FluentStringAssertion<R> asTransformed(Function<String,String> function) { 220 return new FluentStringAssertion<>(this, function.apply(orElse(null)), returns()); 221 } 222 223 /** 224 * Performs the specified regular expression replacement on the underlying string. 225 * 226 * @param regex The regular expression to which this string is to be matched. 227 * @param replacement The string to be substituted for each match. 228 * @return This object. 229 */ 230 public FluentStringAssertion<R> asReplaceAll(String regex, String replacement) { 231 assertArgNotNull("regex", regex); 232 assertArgNotNull("replacement", replacement); 233 return asTransformed(x -> x == null ? null : x.replaceAll(regex, replacement)); 234 } 235 236 /** 237 * Performs the specified substring replacement on the underlying string. 238 * 239 * @param target The sequence of char values to be replaced. 240 * @param replacement The replacement sequence of char values. 241 * @return This object. 242 */ 243 public FluentStringAssertion<R> asReplace(String target, String replacement) { 244 assertArgNotNull("target", target); 245 assertArgNotNull("replacement", replacement); 246 return asTransformed(x -> x == null ? null : x.replace(target, replacement)); 247 } 248 249 /** 250 * URL-decodes the text in this assertion. 251 * 252 * @return This object. 253 */ 254 public FluentStringAssertion<R> asUrlDecode() { 255 return asTransformed(StringUtils::urlDecode); 256 } 257 258 /** 259 * Converts the text to lowercase. 260 * 261 * @return This object. 262 */ 263 public FluentStringAssertion<R> asLc() { 264 return asTransformed(x->x == null ? null : x.toLowerCase()); 265 } 266 267 /** 268 * Converts the text to uppercase. 269 * 270 * @return This object. 271 */ 272 public FluentStringAssertion<R> asUc() { 273 return asTransformed(x->x == null ? null : x.toUpperCase()); 274 } 275 276 /** 277 * Splits the string into lines. 278 * 279 * @return This object. 280 */ 281 public FluentListAssertion<String,R> asLines() { 282 return asSplit("[\r\n]+"); 283 } 284 285 /** 286 * Splits the string into lines using the specified regular expression. 287 * 288 * @param regex The delimiting regular expression 289 * @return This object. 290 */ 291 public FluentListAssertion<String,R> asSplit(String regex) { 292 assertArgNotNull("regex", regex); 293 return new FluentListAssertion<>(this, valueIsNull() ? null : Arrays.asList(value().trim().split(regex)), returns()); 294 } 295 296 /** 297 * Returns the length of this string as an integer assertion. 298 * 299 * @return This object. 300 */ 301 public FluentIntegerAssertion<R> asLength() { 302 return new FluentIntegerAssertion<>(this, valueIsNull() ? null : value().length(), returns()); 303 } 304 305 /** 306 * Removes any newlines from the string. 307 * 308 * @return This object. 309 */ 310 public FluentStringAssertion<R> asOneLine() { 311 return asTransformed(x->x == null ? null : x.replaceAll("\\s*[\r\n]+\\s*"," ")); 312 } 313 314 /** 315 * Removes any leading/trailing whitespace from the string. 316 * 317 * @return This object. 318 */ 319 public FluentStringAssertion<R> asTrimmed() { 320 return new FluentStringAssertion<>(this, valueIsNull() ? null : value().trim(), returns()); 321 } 322 323 //----------------------------------------------------------------------------------------------------------------- 324 // Test methods 325 //----------------------------------------------------------------------------------------------------------------- 326 327 /** 328 * Asserts that the text equals the specified value. 329 * 330 * <p> 331 * Similar to {@link #is(String)} except error message states diff position. 332 * 333 * <h5 class='section'>Example:</h5> 334 * <p class='bjava'> 335 * <jc>// Validates the response body of an HTTP call is the text "OK".</jc> 336 * <jv>client</jv> 337 * .get(<jsf>URL</jsf>) 338 * .run() 339 * .assertContent().is(<js>"OK"</js>); 340 * </p> 341 * 342 * @param value 343 * The value to check against. 344 * <br>If multiple values are specified, they are concatenated with newlines. 345 * @return The fluent return object. 346 * @throws AssertionError If assertion failed. 347 */ 348 @Override 349 public R is(String value) throws AssertionError { 350 String s = orElse(null); 351 if (ne(value, s)) 352 throw error(MSG_stringDifferedAtPosition, diffPosition(value, s), fix(value), fix(s)); 353 return returns(); 354 } 355 356 /** 357 * Asserts that the text equals the specified value. 358 * 359 * @param value The value to check against. 360 * @return The fluent return object. 361 * @throws AssertionError If assertion failed. 362 */ 363 @Override 364 public R isNot(String value) throws AssertionError { 365 String s = orElse(null); 366 if (eq(value, s)) 367 throw error(MSG_stringEqualedUnexpected, fix(s)); 368 return returns(); 369 } 370 371 /** 372 * Asserts that the lines of text equals the specified value. 373 * 374 * <h5 class='section'>Example:</h5> 375 * <p class='bjava'> 376 * <jc>// Validates the response body of an HTTP call is the text "OK".</jc> 377 * <jv>client</jv> 378 * .get(<jsf>URL</jsf>) 379 * .run() 380 * .assertContent().isLines( 381 * <js>"Line 1"</js>, 382 * <js>"Line 2"</js>, 383 * <js>"Line 3"</js> 384 * ); 385 * </p> 386 * 387 * @param lines 388 * The value to check against. 389 * <br>If multiple values are specified, they are concatenated with newlines. 390 * @return The fluent return object. 391 * @throws AssertionError If assertion failed. 392 */ 393 public R isLines(String...lines) throws AssertionError { 394 assertArgNotNull("lines", lines); 395 String v = join(lines, '\n'); 396 String s = value(); 397 if (ne(v, s)) 398 throw error(MSG_stringDifferedAtPosition, diffPosition(v, s), fix(v), fix(s)); 399 return returns(); 400 } 401 402 /** 403 * Asserts that the text equals the specified value after splitting both by newlines and sorting the rows. 404 * 405 * <h5 class='section'>Example:</h5> 406 * <p class='bjava'> 407 * <jc>// Validates the response body of an HTTP call is the text "OK".</jc> 408 * <jv>client</jv> 409 * .get(<jsf>URL</jsf>) 410 * .run() 411 * .assertContent().isSortedLines( 412 * <js>"Line 1"</js>, 413 * <js>"Line 2"</js>, 414 * <js>"Line 3"</js> 415 * ); 416 * </p> 417 * 418 * @param lines 419 * The value to check against. 420 * <br>If multiple values are specified, they are concatenated with newlines. 421 * @return The fluent return object. 422 * @throws AssertionError If assertion failed. 423 */ 424 public R isSortedLines(String...lines) { 425 assertArgNotNull("lines", lines); 426 427 // Must work for windows too. 428 String[] e = StringUtils.join(lines, '\n').trim().split("[\r\n]+"), a = value().trim().split("[\r\n]+"); 429 430 if (e.length != a.length) 431 throw error(MSG_expectedStringHadDifferentNumbersOfLines, e.length, a.length); 432 433 Arrays.sort(e); 434 Arrays.sort(a); 435 436 for (int i = 0; i < e.length; i++) 437 if (! e[i].equals(a[i])) 438 throw error(MSG_expectedStringHadDifferentValuesAtLine, i+1, e[i], a[i]); 439 440 return returns(); 441 } 442 443 /** 444 * Asserts that the text equals the specified value ignoring case. 445 * 446 * @param value The value to check against. 447 * @return The fluent return object. 448 * @throws AssertionError If assertion failed. 449 */ 450 public R isIc(String value) throws AssertionError { 451 String s = orElse(null); 452 if (neic(value, s)) 453 throw error(MSG_stringDifferedAtPosition, diffPositionIc(value, s), fix(value), fix(s)); 454 return returns(); 455 } 456 457 /** 458 * Asserts that the text does not equal the specified value ignoring case. 459 * 460 * @param value The value to check against. 461 * @return The fluent return object. 462 * @throws AssertionError If assertion failed. 463 */ 464 public R isNotIc(String value) throws AssertionError { 465 String s = orElse(null); 466 if (eqic(value, s)) 467 throw error(MSG_stringEqualedUnexpected, fix(s)); 468 return returns(); 469 } 470 471 /** 472 * Asserts that the text contains all of the specified substrings. 473 * 474 * @param values The values to check against. 475 * @return The fluent return object. 476 * @throws AssertionError If assertion failed. 477 */ 478 public R isContains(String...values) throws AssertionError { 479 assertArgNotNull("values", values); 480 String s = orElse(null); 481 for (String substring : values) 482 if (substring != null && ! StringUtils.contains(s, substring)) 483 throw error(MSG_stringDidNotContainExpectedSubstring, fix(substring), fix(s)); 484 return returns(); 485 } 486 487 /** 488 * Asserts that the text doesn't contain any of the specified substrings. 489 * 490 * @param values The values to check against. 491 * @return The fluent return object. 492 * @throws AssertionError If assertion failed. 493 */ 494 public R isNotContains(String...values) throws AssertionError { 495 assertArgNotNull("values", values); 496 String s = orElse(null); 497 for (String substring : values) 498 if (substring != null && StringUtils.contains(s, substring)) 499 throw error(MSG_stringContainedUnexpectedSubstring, fix(substring), fix(s)); 500 return returns(); 501 } 502 503 /** 504 * Asserts that the text is empty. 505 * 506 * @return The fluent return object. 507 * @throws AssertionError If assertion failed. 508 */ 509 public R isEmpty() throws AssertionError { 510 String s = orElse(null); 511 if (s != null && ! s.isEmpty()) 512 throw error(MSG_stringWasNotEmpty, fix(s)); 513 return returns(); 514 } 515 516 /** 517 * Asserts that the text is not null or empty. 518 * 519 * @return The fluent return object. 520 * @throws AssertionError If assertion failed. 521 */ 522 public R isNotEmpty() throws AssertionError { 523 String s = orElse(null); 524 if (s == null) 525 throw error(MSG_stringWasNull); 526 if (s.isEmpty()) 527 throw error(MSG_stringWasEmpty); 528 return returns(); 529 } 530 531 /** 532 * Asserts that the text matches the specified pattern containing <js>"*"</js> meta characters. 533 * 534 * <p> 535 * The <js>"*"</js> meta character can be used to represent zero or more characters.. 536 * 537 * @param searchPattern The search pattern. 538 * @return The fluent return object. 539 * @throws AssertionError If assertion failed. 540 */ 541 public R isMatches(String searchPattern) throws AssertionError { 542 assertArgNotNull("searchPattern", searchPattern); 543 return isPattern(getMatchPattern(searchPattern)); 544 } 545 546 /** 547 * Asserts that the text matches the specified regular expression. 548 * 549 * @param regex The pattern to test for. 550 * @return The fluent return object. 551 * @throws AssertionError If assertion failed. 552 */ 553 public R isPattern(String regex) throws AssertionError { 554 return isPattern(regex, 0); 555 } 556 557 /** 558 * Asserts that the text matches the specified regular expression. 559 * 560 * @param regex The pattern to test for. 561 * @param flags Pattern match flags. See {@link Pattern#compile(String, int)}. 562 * @return The fluent return object. 563 * @throws AssertionError If assertion failed. 564 */ 565 public R isPattern(String regex, int flags) throws AssertionError { 566 assertArgNotNull("regex", regex); 567 Pattern p = Pattern.compile(regex, flags); 568 String s = value(); 569 if (! p.matcher(s).matches()) 570 throw error(MSG_stringDidNotMatchExpectedPattern, fix(regex), fix(s)); 571 return returns(); 572 } 573 574 /** 575 * Asserts that the text matches the specified regular expression pattern. 576 * 577 * @param pattern The pattern to test for. 578 * @return The fluent return object. 579 * @throws AssertionError If assertion failed. 580 */ 581 public R isPattern(Pattern pattern) throws AssertionError { 582 assertArgNotNull("pattern", pattern); 583 String s = value(); 584 if (! pattern.matcher(s).matches()) 585 throw error(MSG_stringDidNotMatchExpectedPattern, fix(pattern.pattern()), fix(s)); 586 return returns(); 587 } 588 589 /** 590 * Asserts that the text starts with the specified string. 591 * 592 * @param string The string to test for. 593 * @return The fluent return object. 594 * @throws AssertionError If assertion failed. 595 */ 596 public R isStartsWith(String string) { 597 assertArgNotNull("string", string); 598 String s = value(); 599 if (! s.startsWith(string)) 600 throw error(MSG_stringDidNotStartWithExpected, fix(string), fix(s)); 601 return returns(); 602 } 603 604 /** 605 * Asserts that the text ends with the specified string. 606 * 607 * @param string The string to test for. 608 * @return The fluent return object. 609 * @throws AssertionError If assertion failed. 610 */ 611 public R isEndsWith(String string) { 612 assertArgNotNull("string", string); 613 String s = value(); 614 if (! s.endsWith(string)) 615 throw error(MSG_stringDidNotEndWithExpected, fix(string), fix(s)); 616 return returns(); 617 } 618 619 /** 620 * Asserts that the text equals the specified object after calling {@link #toString()} on the object. 621 * 622 * @param value The value to check against. 623 * @return The fluent return object. 624 */ 625 public R isString(Object value) { 626 return is(value == null ? null : toString()); 627 } 628 629 //----------------------------------------------------------------------------------------------------------------- 630 // Fluent setters 631 //----------------------------------------------------------------------------------------------------------------- 632 633 // <FluentSetters> 634 635 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 636 public FluentStringAssertion<R> setMsg(String msg, Object...args) { 637 super.setMsg(msg, args); 638 return this; 639 } 640 641 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 642 public FluentStringAssertion<R> setOut(PrintStream value) { 643 super.setOut(value); 644 return this; 645 } 646 647 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 648 public FluentStringAssertion<R> setSilent() { 649 super.setSilent(); 650 return this; 651 } 652 653 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 654 public FluentStringAssertion<R> setStdOut() { 655 super.setStdOut(); 656 return this; 657 } 658 659 @Override /* GENERATED - org.apache.juneau.assertions.Assertion */ 660 public FluentStringAssertion<R> setThrowable(Class<? extends java.lang.RuntimeException> value) { 661 super.setThrowable(value); 662 return this; 663 } 664 665 // </FluentSetters> 666 667 //------------------------------------------------------------------------------------------------------------------ 668 // Utility methods 669 //------------------------------------------------------------------------------------------------------------------ 670 671 private String fix(String text) { 672 if (javaStrings) 673 text = text.replaceAll("\\\\", "\\\\\\\\").replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t"); 674 return text; 675 } 676}