diff --git a/src/main/java/javax/util/streamex/Joining.java b/src/main/java/javax/util/streamex/Joining.java index 39869252..2f08969c 100644 --- a/src/main/java/javax/util/streamex/Joining.java +++ b/src/main/java/javax/util/streamex/Joining.java @@ -31,11 +31,12 @@ import static javax.util.streamex.StreamExInternals.*; /** - * An advanced implementation of joining {@link Collector} which is capable to - * join the input {@code CharSequence} elements with given delimiter optionally - * wrapping into given prefix and suffix and optionally limiting the length of - * the resulting string (in UTF-16 chars, codepoints or Unicode symbols) adding - * the specified ellipsis symbol. This collector supercedes the standard JDK + * An advanced implementation of joining {@link Collector}. This collector is + * capable to join the input {@code CharSequence} elements with given delimiter + * optionally wrapping into given prefix and suffix and optionally limiting the + * length of the resulting string (in Unicode code units, code points or + * grapheme clusters) adding the specified ellipsis sequence. This collector + * supersedes the standard JDK * {@link Collectors#joining(CharSequence, CharSequence, CharSequence)} * collectors family. * @@ -62,8 +63,8 @@ * *

* The intermediate accumulation type of this collector is the implementation - * detail and not exposed to the API. If you want to cast it to {@code Collector} - * type, use ? as accumulator type variable: + * detail and not exposed to the API. If you want to cast it to + * {@code Collector} type, use ? as accumulator type variable: * *

{@code
  * Collector joining = Joining.with(", ");
@@ -80,14 +81,14 @@ static final class Accumulator {
 
     private static final int CUT_ANYWHERE = 0;
     private static final int CUT_CODEPOINT = 1;
-    private static final int CUT_SYMBOL = 2;
+    private static final int CUT_GRAPHEME = 2;
     private static final int CUT_WORD = 3;
     private static final int CUT_BEFORE_DELIMITER = 4;
     private static final int CUT_AFTER_DELIMITER = 5;
 
     private static final int LENGTH_CHARS = 0;
     private static final int LENGTH_CODEPOINTS = 1;
-    private static final int LENGTH_SYMBOLS = 2;
+    private static final int LENGTH_GRAPHEMES = 2;
 
     private final String delimiter, ellipsis, prefix, suffix;
     private final int cutStrategy, lenStrategy, maxLength;
@@ -103,9 +104,9 @@ private Joining(String delimiter, String ellipsis, String prefix, String suffix,
         this.lenStrategy = lenStrategy;
         this.maxLength = maxLength;
     }
-    
+
     private void init() {
-        if(delimCount == -1) {
+        if (delimCount == -1) {
             limit = maxLength - length(prefix) - length(suffix);
             delimCount = length(delimiter);
         }
@@ -119,7 +120,7 @@ private int length(CharSequence s) {
             if (s instanceof String)
                 return ((String) s).codePointCount(0, s.length());
             return (int) s.codePoints().count();
-        case LENGTH_SYMBOLS:
+        case LENGTH_GRAPHEMES:
             BreakIterator bi = BreakIterator.getCharacterInstance();
             bi.setText(s.toString());
             int count = 0;
@@ -149,10 +150,11 @@ private int copyCut(char[] buf, int pos, String str, int limit, int cutStrategy)
             if (limit < str.codePointCount(0, str.length()))
                 endPos = str.offsetByCodePoints(0, limit);
             break;
-        case LENGTH_SYMBOLS:
+        case LENGTH_GRAPHEMES:
             BreakIterator bi = BreakIterator.getCharacterInstance();
             bi.setText(str);
-            int count = limit, end = 0;
+            int count = limit,
+            end = 0;
             while (true) {
                 end = bi.next();
                 if (end == BreakIterator.DONE)
@@ -178,7 +180,7 @@ private int copyCut(char[] buf, int pos, String str, int limit, int cutStrategy)
                 bi.setText(str);
                 endPos = bi.preceding(endPos + 1);
                 break;
-            case CUT_SYMBOL:
+            case CUT_GRAPHEME:
                 bi = BreakIterator.getCharacterInstance();
                 bi.setText(str);
                 endPos = bi.preceding(endPos + 1);
@@ -220,52 +222,266 @@ private Joining withLimit(int lenStrategy, int maxLength) {
     private Joining withCut(int cutStrategy) {
         return new Joining(delimiter, ellipsis, prefix, suffix, cutStrategy, lenStrategy, maxLength);
     }
-    
+
+    /**
+     * Returns a {@code Collector} that concatenates the input elements,
+     * separated by the specified delimiter, in encounter order.
+     * 
+     * 

+ * This collector is similar to {@link Collectors#joining(CharSequence)}, + * but can be further set up in a flexible way, for example, specifying the + * maximal allowed length of the resulting {@code String}. + * + * @param delimiter + * the delimiter to be used between each element + * @return A {@code Collector} which concatenates CharSequence elements, + * separated by the specified delimiter, in encounter order + * @see Collectors#joining(CharSequence) + */ public static Joining with(CharSequence delimiter) { - return new Joining(delimiter.toString(), "...", "", "", CUT_CODEPOINT, LENGTH_CHARS, -1); + return new Joining(delimiter.toString(), "...", "", "", CUT_GRAPHEME, LENGTH_CHARS, -1); } + /** + * Returns a {@code Collector} which behaves like this collector, but + * additionally wraps the result with the specified prefix and suffix. + * + *

+ * The collector returned by + * {@code Joining.with(delimiter).wrap(prefix, suffix)} is equivalent to + * {@link Collectors#joining(CharSequence, CharSequence, CharSequence)}, but + * can be further set up in a flexible way, for example, specifying the + * maximal allowed length of the resulting {@code String}. + * + *

+ * If length limit is specified for the collector, the prefix length and the + * suffix length are also counted towards this limit. If the length of the + * prefix and the suffix exceed the limit, the resulting collector will not + * accumulate any elements and produce the same output. For example, + * {@code stream.collect(Joining.with(",").wrap("prefix", "suffix").maxChars(9))} + * will produce {@code "prefixsuf"} string regardless of the input stream + * content. + * + *

+ * You may wrap several times: + * {@code Joining.with(",").wrap("[", "]").wrap("(", ")")} is equivalent to + * {@code Joining.with(",").wrap("([", "])")}. + * + * @param prefix + * the sequence of characters to be used at the beginning of the + * joined result + * @param suffix + * the sequence of characters to be used at the end of the joined + * result + * @return a new {@code Collector} which wraps the result with the specified + * prefix and suffix. + */ public Joining wrap(CharSequence prefix, CharSequence suffix) { return new Joining(delimiter, ellipsis, prefix.toString().concat(this.prefix), this.suffix.concat(suffix .toString()), cutStrategy, lenStrategy, maxLength); } + /** + * Returns a {@code Collector} which behaves like this collector, but uses + * the specified ellipsis {@code CharSequence} instead of default + * {@code "..."} when the string limit (if specified) is reached. + * + * @param ellipsis + * the sequence of characters to be used at the end of the joined + * result to designate that not all of the input elements are + * joined due to the specified string length restriction. + * @return a new {@code Collector} which will use the specified ellipsis + * instead of current setting. + */ public Joining ellipsis(CharSequence ellipsis) { return new Joining(delimiter, ellipsis.toString(), prefix, suffix, cutStrategy, lenStrategy, maxLength); } + /** + * Returns a {@code Collector} which behaves like this collector, but sets + * the maximal length of the resulting string to the specified number of + * UTF-16 characters (or Unicode code units). This setting overwrites any + * limit previously set by {@link #maxChars(int)}, + * {@link #maxCodePoints(int)} or {@link #maxGraphemes(int)} call. + * + *

+ * The {@code String} produced by the resulting collector is guaranteed to + * have {@link String#length() length} which does not exceed the specified + * limit. An ellipsis sequence (by default {@code "..."}) is used to + * designate whether the limit was reached. Use + * {@link #ellipsis(CharSequence)} to set custom ellipsis sequence. + * + *

+ * The collector returned by this method is short-circuiting: + * it may not process all the input elements if the limit is reached. + * + * @param limit + * the maximal number of UTF-16 characters in the resulting + * String. + * @return a new {@code Collector} which will produce String no longer than + * given limit. + */ public Joining maxChars(int limit) { return withLimit(LENGTH_CHARS, limit); } + /** + * Returns a {@code Collector} which behaves like this collector, but sets + * the maximal number of Unicode code points of the resulting string. This + * setting overwrites any limit previously set by {@link #maxChars(int)}, + * {@link #maxCodePoints(int)} or {@link #maxGraphemes(int)} call. + * + *

+ * The {@code String} produced by the resulting collector is guaranteed to + * have no more code points than the specified limit. An ellipsis sequence + * (by default {@code "..."}) is used to designate whether the limit was + * reached. Use {@link #ellipsis(CharSequence)} to set custom ellipsis + * sequence. + * + *

+ * The collector returned by this method is short-circuiting: + * it may not process all the input elements if the limit is reached. + * + * @param limit + * the maximal number of code points in the resulting String. + * @return a new {@code Collector} which will produce String no longer than + * given limit. + */ public Joining maxCodePoints(int limit) { return withLimit(LENGTH_CODEPOINTS, limit); } - public Joining maxSymbols(int limit) { - return withLimit(LENGTH_SYMBOLS, limit); + /** + * Returns a {@code Collector} which behaves like this collector, but sets + * the maximal number of grapheme clusters. This setting overwrites any + * limit previously set by {@link #maxChars(int)}, + * {@link #maxCodePoints(int)} or {@link #maxGraphemes(int)} call. + * + *

+ * The grapheme cluster is defined in {@linkplain http + * ://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries Unicode Text + * Segmentation} technical report. Basically, it counts base character and + * the following combining characters as single object. The {@code String} + * produced by the resulting collector is guaranteed to have no more + * grapheme clusters than the specified limit. An ellipsis sequence (by + * default {@code "..."}) is used to designate whether the limit was + * reached. Use {@link #ellipsis(CharSequence)} to set custom ellipsis + * sequence. + * + *

+ * The collector returned by this method is short-circuiting: + * it may not process all the input elements if the limit is reached. + * + * @param limit + * the maximal number of grapheme clusters in the resulting + * String. + * @return a new {@code Collector} which will produce String no longer than + * given limit. + */ + public Joining maxGraphemes(int limit) { + return withLimit(LENGTH_GRAPHEMES, limit); } + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string at any point when limit is reached. + * + *

+ * The resulting collector will produce {@code String} which length is + * exactly equal to the specified limit if the limit is reached. If used + * with {@link #maxChars(int)}, the resulting string may be cut in the + * middle of surrogate pair. + * + * @return a new {@code Collector} which cuts the resulting string at any + * point when limit is reached. + */ public Joining cutAnywhere() { return withCut(CUT_ANYWHERE); } - public Joining cutAfterCodePoint() { + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string between any code points when limit is reached. + * + *

+ * The resulting collector will not split the surrogate pair when used with + * {@link #maxChars(int)} or {@link #maxCodePoints(int)}. However it may + * remove the combining character which may result in incorrect rendering of + * the last displayed grapheme. + * + * @return a new {@code Collector} which cuts the resulting string between + * code points. + */ + public Joining cutAtCodePoint() { return withCut(CUT_CODEPOINT); } - public Joining cutAfterSymbol() { - return withCut(CUT_SYMBOL); + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string at grapheme cluster boundary when limit is reached. + * This is the default behavior. + * + *

+ * The grapheme cluster is defined in {@linkplain http + * ://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries Unicode Text + * Segmentation} technical report. Thus the resulting collector will not + * split the surrogate pair and will preserve any combining characters or + * remove them with the base character. + * + * @return a new {@code Collector} which cuts the resulting string at + * grapheme cluster boundary. + */ + public Joining cutAtGrapheme() { + return withCut(CUT_GRAPHEME); } - public Joining cutAfterWord() { + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string at word boundary when limit is reached. + * + *

+ * The beginning and end of every input stream element or delimiter is + * always considered as word boundary, so the stream of + * {@code "one", "two three"} collected with + * {@code Joining.with("").maxChars(n).ellipsis("").cutAfterWord()} may + * produce the following strings depending on {@code n}: + * + *

{@code
+     * ""
+     * "one"
+     * "onetwo"
+     * "onetwo "
+     * "onetwo three"
+     * }
+ * + * @return a new {@code Collector} which cuts the resulting string at word + * boundary. + */ + public Joining cutAtWord() { return withCut(CUT_WORD); } + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string before the delimiter when limit is reached. + * + * @return a new {@code Collector} which cuts the resulting string at before + * the delimiter. + */ public Joining cutBeforeDelimiter() { return withCut(CUT_BEFORE_DELIMITER); } + /** + * Returns a {@code Collector} which behaves like this collector, but cuts + * the resulting string after the delimiter when limit is reached. + * + * @return a new {@code Collector} which cuts the resulting string at after + * the delimiter. + */ public Joining cutAfterDelimiter() { return withCut(CUT_AFTER_DELIMITER); } @@ -332,7 +548,7 @@ public BinaryOperator combiner() { return acc1; }; } - + @Override public Function finisher() { if (maxLength == -1) { diff --git a/src/test/java/javax/util/streamex/JoiningTest.java b/src/test/java/javax/util/streamex/JoiningTest.java index c3d8570d..4c757817 100644 --- a/src/test/java/javax/util/streamex/JoiningTest.java +++ b/src/test/java/javax/util/streamex/JoiningTest.java @@ -46,7 +46,7 @@ public void testMaxCodePointsRange() { @Test(expected = IllegalArgumentException.class) public void testMaxSymbolsRange() { - Joining.with(",").maxSymbols(Integer.MIN_VALUE); + Joining.with(",").maxGraphemes(Integer.MIN_VALUE); } @Test @@ -113,16 +113,16 @@ public void testCuts() { .cutAfterDelimiter()); checkShortCircuitCollector("cutWord", "one two, three four, ...", 4, input::stream, Joining.with(", ") - .maxChars(25).cutAfterWord()); + .maxChars(25).cutAtWord()); checkShortCircuitCollector("cutWord", "one two, ...", 2, input::stream, Joining.with(", ").maxChars(12) - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "one two,...", 2, input::stream, Joining.with(", ").maxChars(11) - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "one two...", 2, input::stream, Joining.with(", ").maxChars(10) - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "one ...", 2, input::stream, Joining.with(", ").maxChars(9) - .cutAfterWord()); - checkShortCircuitCollector("cutWord", "one...", 1, input::stream, Joining.with(", ").maxChars(6).cutAfterWord()); + .cutAtWord()); + checkShortCircuitCollector("cutWord", "one...", 1, input::stream, Joining.with(", ").maxChars(6).cutAtWord()); checkShortCircuitCollector("cutCodePoint", "one two, three four, f...", 4, input::stream, Joining.with(", ") .maxChars(25)); @@ -137,34 +137,34 @@ public void testCuts() { public void testPrefixSuffix() { List input = Arrays.asList("one two", "three four", "five", "six seven"); checkShortCircuitCollector("cutWord", "[one two, three four,...]", 3, input::stream, - Joining.with(", ").wrap("[", "]").maxChars(25).cutAfterWord()); + Joining.with(", ").wrap("[", "]").maxChars(25).cutAtWord()); checkShortCircuitCollector("cutWord", "[one two...]", 2, input::stream, - Joining.with(", ").maxChars(12).wrap("[", "]").cutAfterWord()); + Joining.with(", ").maxChars(12).wrap("[", "]").cutAtWord()); checkShortCircuitCollector("cutWord", "[one ...]", 2, input::stream, - Joining.with(", ").maxChars(11).wrap("[", "]").cutAfterWord()); + Joining.with(", ").maxChars(11).wrap("[", "]").cutAtWord()); checkShortCircuitCollector("cutWord", "[one ...]", 2, input::stream, - Joining.with(", ").maxChars(10).wrap("[", "]").cutAfterWord()); + Joining.with(", ").maxChars(10).wrap("[", "]").cutAtWord()); checkShortCircuitCollector("cutWord", "[one...]", 1, input::stream, - Joining.with(", ").maxChars(8).wrap("[", "]").cutAfterWord()); + Joining.with(", ").maxChars(8).wrap("[", "]").cutAtWord()); checkShortCircuitCollector("cutWord", "[...]", 1, input::stream, Joining.with(", ").maxChars(6).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "[..]", 1, input::stream, Joining.with(", ").maxChars(4).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "[.]", 1, input::stream, Joining.with(", ").maxChars(3).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "[]", 0, input::stream, Joining.with(", ").maxChars(2).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "[", 0, input::stream, Joining.with(", ").maxChars(1).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "", 0, input::stream, Joining.with(", ").maxChars(0).wrap("[", "]") - .cutAfterWord()); + .cutAtWord()); checkShortCircuitCollector("cutWord", "a prefix a ", 0, input::stream, - Joining.with(" ").maxChars(15).wrap("a prefix ", " a suffix").cutAfterWord()); + Joining.with(" ").maxChars(15).wrap("a prefix ", " a suffix").cutAtWord()); checkShortCircuitCollector("cutWord", "a prefix ", 0, input::stream, - Joining.with(" ").maxChars(10).wrap("a prefix ", " a suffix").cutAfterWord()); + Joining.with(" ").maxChars(10).wrap("a prefix ", " a suffix").cutAtWord()); checkShortCircuitCollector("cutWord", "a ", 0, input::stream, - Joining.with(" ").maxChars(5).wrap("a prefix ", " a suffix").cutAfterWord()); + Joining.with(" ").maxChars(5).wrap("a prefix ", " a suffix").cutAtWord()); } @Test @@ -174,30 +174,30 @@ public void testCodePoints() { checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f\ud801", 1, input::stream, Joining.with(",") .ellipsis("").maxChars(5).cutAnywhere()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f", 1, input::stream, Joining.with(",") - .ellipsis("").maxChars(5).cutAfterCodePoint()); + .ellipsis("").maxChars(5).cutAtCodePoint()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f", 1, input::stream, Joining.with(",") - .ellipsis("").maxChars(4).cutAfterSymbol()); + .ellipsis("").maxChars(4).cutAtGrapheme()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f", 1, input::stream, Joining.with(",") .ellipsis("").maxChars(4).cutAnywhere()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f", 1, input::stream, Joining.with(",") - .ellipsis("").maxChars(4).cutAfterCodePoint()); + .ellipsis("").maxChars(4).cutAtCodePoint()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f", 1, input::stream, Joining.with(",") - .ellipsis("").maxChars(4).cutAfterSymbol()); + .ellipsis("").maxChars(4).cutAtGrapheme()); checkShortCircuitCollector("maxChars", "\ud801\udc14\ud801\udc2f\ud801\udc45\ud801\udc28", 1, input::stream, Joining.with(",").ellipsis("").maxChars(9)); checkShortCircuitCollector("maxCodePoints", "\ud801\udc14\ud801\udc2f\ud801\udc45\ud801\udc28\ud801\udc49", 1, input::stream, Joining.with(",").ellipsis("").maxCodePoints(5).cutAnywhere()); checkShortCircuitCollector("maxCodePoints", "\ud801\udc14\ud801\udc2f\ud801\udc45\ud801\udc28\ud801\udc49", 1, - input::stream, Joining.with(",").ellipsis("").maxCodePoints(5).cutAfterCodePoint()); + input::stream, Joining.with(",").ellipsis("").maxCodePoints(5).cutAtCodePoint()); checkShortCircuitCollector("maxCodePoints", "\ud801\udc14\ud801\udc2f\ud801\udc45\ud801\udc28\ud801\udc49", 1, - input::stream, Joining.with(",").ellipsis("").maxCodePoints(5).cutAfterSymbol()); + input::stream, Joining.with(",").ellipsis("").maxCodePoints(5).cutAtGrapheme()); checkShortCircuitCollector("maxCodePoints", string, 2, input::stream, Joining.with(",").ellipsis("") .maxCodePoints(7)); checkShortCircuitCollector("maxCodePoints", string + ",", 2, input::stream, Joining.with(",").ellipsis("") .maxCodePoints(8)); checkShortCircuitCollector("maxCodePoints", string + ",\ud801\udc14", 2, input::stream, Joining.with(",") - .ellipsis("").maxCodePoints(9).cutAfterCodePoint()); + .ellipsis("").maxCodePoints(9).cutAtCodePoint()); checkShortCircuitCollector("maxCodePoints", string + "," + string + "," + string, 3, input::stream, Joining .with(",").ellipsis("").maxCodePoints(23)); checkShortCircuitCollector("maxCodePoints", string + "," + string @@ -216,67 +216,67 @@ public void testSurrogates() { checkShortCircuitCollector("highSurr", string.substring(0, 4), 1, input::stream, Joining.with(",").ellipsis("") .maxChars(4).cutAnywhere()); checkShortCircuitCollector("highSurr", string.substring(0, 4), 1, input::stream, Joining.with(",").ellipsis("") - .maxChars(4).cutAfterCodePoint()); + .maxChars(4).cutAtCodePoint()); checkShortCircuitCollector("highSurr", string.substring(0, 4), 1, input::stream, Joining.with(",").ellipsis("") .maxCodePoints(4).cutAnywhere()); checkShortCircuitCollector("lowSurr", string.substring(0, 7), 1, input::stream, Joining.with(",").ellipsis("") .maxChars(7).cutAnywhere()); checkShortCircuitCollector("lowSurr", string.substring(0, 7), 1, input::stream, Joining.with(",").ellipsis("") - .maxChars(7).cutAfterCodePoint()); + .maxChars(7).cutAtCodePoint()); checkShortCircuitCollector("lowSurr", string.substring(0, 8), 1, input::stream, Joining.with(",").ellipsis("") .maxCodePoints(7).cutAnywhere()); } @Test - public void testSymbols() { + public void testGraphemes() { String string = "aa\u0300\u0321e\u0300a\u0321a\u0300\u0321a"; List input = Collections.nCopies(3, string); checkShortCircuitCollector("maxChars", "aa\u0300\u0321e", 1, input::stream, Joining.with(",").ellipsis("") .maxChars(5).cutAnywhere()); checkShortCircuitCollector("maxChars", "aa\u0300\u0321e", 1, input::stream, Joining.with(",").ellipsis("") - .maxChars(5).cutAfterCodePoint()); + .maxChars(5).cutAtCodePoint()); checkShortCircuitCollector("maxChars", "aa\u0300\u0321", 1, input::stream, Joining.with(",").ellipsis("") - .maxChars(5).cutAfterSymbol()); + .maxChars(5).cutAtGrapheme()); checkShortCircuitCollector("maxChars", "aa\u0300\u0321e\u0300", 1, input::stream, Joining.with(",") - .ellipsis("").maxChars(6).cutAfterSymbol()); + .ellipsis("").maxChars(6).cutAtGrapheme()); checkShortCircuitCollector("maxChars", "aa\u0300\u0321", 1, input::stream, Joining.with(",").ellipsis("") - .maxChars(4).cutAfterSymbol()); + .maxChars(4).cutAtGrapheme()); checkShortCircuitCollector("maxChars", "a", 1, input::stream, Joining.with(",").ellipsis("").maxChars(3) - .cutAfterSymbol()); + .cutAtGrapheme()); checkShortCircuitCollector("maxSymbols", "aa\u0300\u0321e\u0300", 1, input::stream, - Joining.with(",").ellipsis("").maxSymbols(3)); + Joining.with(",").ellipsis("").maxGraphemes(3)); checkShortCircuitCollector("maxSymbols", "aa\u0300\u0321e\u0300a\u0321a\u0300\u0321", 1, input::stream, Joining - .with(",").ellipsis("").maxSymbols(5)); - checkShortCircuitCollector("maxSymbols", string, 2, input::stream, Joining.with(",").ellipsis("").maxSymbols(6)); + .with(",").ellipsis("").maxGraphemes(5)); + checkShortCircuitCollector("maxSymbols", string, 2, input::stream, Joining.with(",").ellipsis("").maxGraphemes(6)); checkShortCircuitCollector("maxSymbols", string + ",", 2, input::stream, Joining.with(",").ellipsis("") - .maxSymbols(7)); + .maxGraphemes(7)); checkShortCircuitCollector("maxSymbols", string + ",a", 2, input::stream, Joining.with(",").ellipsis("") - .maxSymbols(8)); + .maxGraphemes(8)); checkShortCircuitCollector("maxSymbols", string + ",aa\u0300\u0321", 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(9)); + .ellipsis("").maxGraphemes(9)); checkShortCircuitCollector("maxSymbolsBeforeDelimiter", "", 1, input::stream, Joining.with(",").ellipsis("") - .maxSymbols(5).cutBeforeDelimiter()); + .maxGraphemes(5).cutBeforeDelimiter()); checkShortCircuitCollector("maxSymbolsBeforeDelimiter", string, 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(6).cutBeforeDelimiter()); + .ellipsis("").maxGraphemes(6).cutBeforeDelimiter()); checkShortCircuitCollector("maxSymbolsBeforeDelimiter", string, 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(7).cutBeforeDelimiter()); + .ellipsis("").maxGraphemes(7).cutBeforeDelimiter()); checkShortCircuitCollector("maxSymbolsBeforeDelimiter", string, 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(8).cutBeforeDelimiter()); + .ellipsis("").maxGraphemes(8).cutBeforeDelimiter()); checkShortCircuitCollector("maxSymbolsAfterDelimiter", "", 1, input::stream, Joining.with(",").ellipsis("") - .maxSymbols(5).cutAfterDelimiter()); + .maxGraphemes(5).cutAfterDelimiter()); checkShortCircuitCollector("maxSymbolsAfterDelimiter", "", 2, input::stream, Joining.with(",").ellipsis("") - .maxSymbols(6).cutAfterDelimiter()); + .maxGraphemes(6).cutAfterDelimiter()); checkShortCircuitCollector("maxSymbolsAfterDelimiter", string + ",", 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(7).cutAfterDelimiter()); + .ellipsis("").maxGraphemes(7).cutAfterDelimiter()); checkShortCircuitCollector("maxSymbolsAfterDelimiter", string + ",", 2, input::stream, Joining.with(",") - .ellipsis("").maxSymbols(8).cutAfterDelimiter()); + .ellipsis("").maxGraphemes(8).cutAfterDelimiter()); checkShortCircuitCollector("maxSymbolsBeforeDelimiterPrefix", string, 0, input::stream, Joining.with(",") - .wrap(string, string).maxSymbols(8).cutBeforeDelimiter()); + .wrap(string, string).maxGraphemes(8).cutBeforeDelimiter()); } }