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