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