1 /*
2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package java.util;
26
27 /**
28 * {@code StringJoiner} is used to construct a sequence of characters separated
29 * by a delimiter and optionally starting with a supplied prefix
30 * and ending with a supplied suffix.
31 * <p>
32 * Prior to adding something to the {@code StringJoiner}, its
33 * {@code sj.toString()} method will, by default, return {@code prefix + suffix}.
34 * However, if the {@code setEmptyValue} method is called, the {@code emptyValue}
35 * supplied will be returned instead. This can be used, for example, when
36 * creating a string using set notation to indicate an empty set, i.e.
37 * <code>"{}"</code>, where the {@code prefix} is <code>"{"</code>, the
38 * {@code suffix} is <code>"}"</code> and nothing has been added to the
39 * {@code StringJoiner}.
40 *
41 * @apiNote
42 * <p>The String {@code "[George:Sally:Fred]"} may be constructed as follows:
43 *
44 * <pre> {@code
45 * StringJoiner sj = new StringJoiner(":", "[", "]");
46 * sj.add("George").add("Sally").add("Fred");
47 * String desiredString = sj.toString();
48 * }</pre>
49 * <p>
50 * A {@code StringJoiner} may be employed to create formatted output from a
51 * {@link java.util.stream.Stream} using
52 * {@link java.util.stream.Collectors#joining(CharSequence)}. For example:
53 *
54 * <pre> {@code
55 * List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
56 * String commaSeparatedNumbers = numbers.stream()
57 * .map(i -> i.toString())
58 * .collect(Collectors.joining(", "));
59 * }</pre>
60 *
61 * @see java.util.stream.Collectors#joining(CharSequence)
62 * @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence)
63 * @since 1.8
64 */
65 public final class StringJoiner {
66 private final String prefix;
67 private final String delimiter;
68 private final String suffix;
69
70 /** Contains all the string components added so far. */
71 private String[] elts;
72
73 /** The number of string components added so far. */
74 private int size;
75
76 /** Total length in chars so far, excluding prefix and suffix. */
77 private int len;
78
79 /**
80 * When overriden by the user to be non-null via {@link setEmptyValue}, the
81 * string returned by toString() when no elements have yet been added.
82 * When null, prefix + suffix is used as the empty value.
83 */
84 private String emptyValue;
85
86 /**
87 * Constructs a {@code StringJoiner} with no characters in it, with no
88 * {@code prefix} or {@code suffix}, and a copy of the supplied
89 * {@code delimiter}.
90 * If no characters are added to the {@code StringJoiner} and methods
91 * accessing the value of it are invoked, it will not return a
92 * {@code prefix} or {@code suffix} (or properties thereof) in the result,
93 * unless {@code setEmptyValue} has first been called.
94 *
95 * @param delimiter the sequence of characters to be used between each
96 * element added to the {@code StringJoiner} value
97 * @throws NullPointerException if {@code delimiter} is {@code null}
98 */
99 public StringJoiner(CharSequence delimiter) {
100 this(delimiter, "", "");
101 }
102
103 /**
104 * Constructs a {@code StringJoiner} with no characters in it using copies
105 * of the supplied {@code prefix}, {@code delimiter} and {@code suffix}.
106 * If no characters are added to the {@code StringJoiner} and methods
107 * accessing the string value of it are invoked, it will return the
108 * {@code prefix + suffix} (or properties thereof) in the result, unless
109 * {@code setEmptyValue} has first been called.
110 *
111 * @param delimiter the sequence of characters to be used between each
112 * element added to the {@code StringJoiner}
113 * @param prefix the sequence of characters to be used at the beginning
114 * @param suffix the sequence of characters to be used at the end
115 * @throws NullPointerException if {@code prefix}, {@code delimiter}, or
116 * {@code suffix} is {@code null}
117 */
118 public StringJoiner(CharSequence delimiter,
119 CharSequence prefix,
120 CharSequence suffix) {
121 Objects.requireNonNull(prefix, "The prefix must not be null");
122 Objects.requireNonNull(delimiter, "The delimiter must not be null");
123 Objects.requireNonNull(suffix, "The suffix must not be null");
124 // make defensive copies of arguments
125 this.prefix = prefix.toString();
126 this.delimiter = delimiter.toString();
127 this.suffix = suffix.toString();
128 }
129
130 /**
131 * Sets the sequence of characters to be used when determining the string
132 * representation of this {@code StringJoiner} and no elements have been
133 * added yet, that is, when it is empty. A copy of the {@code emptyValue}
134 * parameter is made for this purpose. Note that once an add method has been
135 * called, the {@code StringJoiner} is no longer considered empty, even if
136 * the element(s) added correspond to the empty {@code String}.
137 *
138 * @param emptyValue the characters to return as the value of an empty
139 * {@code StringJoiner}
140 * @return this {@code StringJoiner} itself so the calls may be chained
141 * @throws NullPointerException when the {@code emptyValue} parameter is
142 * {@code null}
143 */
144 public StringJoiner setEmptyValue(CharSequence emptyValue) {
145 this.emptyValue = Objects.requireNonNull(emptyValue,
146 "The empty value must not be null").toString();
147 return this;
148 }
149
150 private static int getChars(String s, char[] chars, int start) {
151 int len = s.length();
152 s.getChars(0, len, chars, start);
153 return len;
154 }
155
156 /**
157 * Returns the current value, consisting of the {@code prefix}, the values
158 * added so far separated by the {@code delimiter}, and the {@code suffix},
159 * unless no elements have been added in which case, the
160 * {@code prefix + suffix} or the {@code emptyValue} characters are returned.
161 *
162 * @return the string representation of this {@code StringJoiner}
163 */
164 @Override
165 public String toString() {
166 final String[] elts = this.elts;
167 if (elts == null && emptyValue != null) {
168 return emptyValue;
169 }
170 final int size = this.size;
171 final int addLen = prefix.length() + suffix.length();
172 if (addLen == 0) {
173 compactElts();
174 return size == 0 ? "" : elts[0];
175 }
176 final String delimiter = this.delimiter;
177 final char[] chars = new char[len + addLen];
178 int k = getChars(prefix, chars, 0);
179 if (size > 0) {
180 k += getChars(elts[0], chars, k);
181 for (int i = 1; i < size; i++) {
182 k += getChars(delimiter, chars, k);
183 k += getChars(elts[i], chars, k);
184 }
185 }
186 k += getChars(suffix, chars, k);
187 return new String(chars);
188 }
189
190 /**
191 * Adds a copy of the given {@code CharSequence} value as the next
192 * element of the {@code StringJoiner} value. If {@code newElement} is
193 * {@code null}, then {@code "null"} is added.
194 *
195 * @param newElement The element to add
196 * @return a reference to this {@code StringJoiner}
197 */
198 public StringJoiner add(CharSequence newElement) {
199 final String elt = String.valueOf(newElement);
200 if (elts == null) {
201 elts = new String[8];
202 } else {
203 if (size == elts.length)
204 elts = Arrays.copyOf(elts, 2 * size);
205 len += delimiter.length();
206 }
207 len += elt.length();
208 elts[size++] = elt;
209 return this;
210 }
211
212 /**
213 * Adds the contents of the given {@code StringJoiner} without prefix and
214 * suffix as the next element if it is non-empty. If the given {@code
215 * StringJoiner} is empty, the call has no effect.
216 *
217 * <p>A {@code StringJoiner} is empty if {@link #add(CharSequence) add()}
218 * has never been called, and if {@code merge()} has never been called
219 * with a non-empty {@code StringJoiner} argument.
220 *
221 * <p>If the other {@code StringJoiner} is using a different delimiter,
222 * then elements from the other {@code StringJoiner} are concatenated with
223 * that delimiter and the result is appended to this {@code StringJoiner}
224 * as a single element.
225 *
226 * @param other The {@code StringJoiner} whose contents should be merged
227 * into this one
228 * @throws NullPointerException if the other {@code StringJoiner} is null
229 * @return This {@code StringJoiner}
230 */
231 public StringJoiner merge(StringJoiner other) {
232 Objects.requireNonNull(other);
233 if (other.elts == null) {
234 return this;
235 }
236 other.compactElts();
237 return add(other.elts[0]);
238 }
239
240 private void compactElts() {
241 if (size > 1) {
242 final char[] chars = new char[len];
243 int i = 1, k = getChars(elts[0], chars, 0);
244 do {
245 k += getChars(delimiter, chars, k);
246 k += getChars(elts[i], chars, k);
247 elts[i] = null;
248 } while (++i < size);
249 size = 1;
250 elts[0] = new String(chars);
251 }
252 }
253
254 /**
255 * Returns the length of the {@code String} representation
256 * of this {@code StringJoiner}. Note that if
257 * no add methods have been called, then the length of the {@code String}
258 * representation (either {@code prefix + suffix} or {@code emptyValue})
259 * will be returned. The value should be equivalent to
260 * {@code toString().length()}.
261 *
262 * @return the length of the current value of {@code StringJoiner}
263 */
264 public int length() {
265 return (size == 0 && emptyValue != null) ? emptyValue.length() :
266 len + prefix.length() + suffix.length();
267 }
268 }
269