/* * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.util; /** * {@code StringJoiner} is used to construct a sequence of characters separated * by a delimiter and optionally starting with a supplied prefix * and ending with a supplied suffix. *

* Prior to adding something to the {@code StringJoiner}, its * {@code sj.toString()} method will, by default, return {@code prefix + suffix}. * However, if the {@code setEmptyValue} method is called, the {@code emptyValue} * supplied will be returned instead. This can be used, for example, when * creating a string using set notation to indicate an empty set, i.e. * "{}", where the {@code prefix} is "{", the * {@code suffix} is "}" and nothing has been added to the * {@code StringJoiner}. * * @apiNote *

The String {@code "[George:Sally:Fred]"} may be constructed as follows: * *

 {@code
 * StringJoiner sj = new StringJoiner(":", "[", "]");
 * sj.add("George").add("Sally").add("Fred");
 * String desiredString = sj.toString();
 * }
*

* A {@code StringJoiner} may be employed to create formatted output from a * {@link java.util.stream.Stream} using * {@link java.util.stream.Collectors#toStringJoiner}. For example: * *

 {@code
 * List numbers = Arrays.asList(1, 2, 3, 4);
 * String commaSeparatedNumbers = numbers.stream()
 *     .map(i -> i.toString())
 *     .collect(Collectors.toStringJoiner(", ")).toString();
 * }
* * @see java.util.stream.Collectors#toStringJoiner * @since 1.8 */ public final class StringJoiner { private final String prefix; private final String delimiter; private final String suffix; /* * StringBuilder value -- at any time, the characters constructed from the * prefix, the added element separated by the delimiter, but without the * suffix, so that we can more easily add elements without having to jigger * the suffix each time. */ private StringBuilder value; /* * By default, the string consisting of prefix+suffix, returned by * toString(), or properties of value, when no elements have yet been added, * i.e. when it is empty. This may be overridden by the user to be some * other value including the empty String. */ private String emptyValue; /** * Constructs a {@code StringJoiner} with no characters in it, with no * {@code prefix} or {@code suffix}, and a copy of the supplied * {@code delimiter}. * If no characters are added to the {@code StringJoiner} and methods * accessing the value of it are invoked, it will not return a * {@code prefix} or {@code suffix} (or properties thereof) in the result, * unless {@code setEmptyValue} has first been called. * * @param delimiter the sequence of characters to be used between each * element added to the {@code StringJoiner} value * @throws NullPointerException if {@code delimiter} is {@code null} */ public StringJoiner(CharSequence delimiter) { this(delimiter, "", ""); } /** * Constructs a {@code StringJoiner} with no characters in it using copies * of the supplied {@code prefix}, {@code delimiter} and {@code suffix}. * If no characters are added to the {@code StringJoiner} and methods * accessing the string value of it are invoked, it will return the * {@code prefix + suffix} (or properties thereof) in the result, unless * {@code setEmptyValue} has first been called. * * @param delimiter the sequence of characters to be used between each * element added to the {@code StringJoiner} * @param prefix the sequence of characters to be used at the beginning * @param suffix the sequence of characters to be used at the end * @throws NullPointerException if {@code prefix}, {@code delimiter}, or * {@code suffix} is {@code null} */ public StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { Objects.requireNonNull(prefix, "The prefix must not be null"); Objects.requireNonNull(delimiter, "The delimiter must not be null"); Objects.requireNonNull(suffix, "The suffix must not be null"); // make defensive copies of arguments this.prefix = prefix.toString(); this.delimiter = delimiter.toString(); this.suffix = suffix.toString(); this.emptyValue = this.prefix + this.suffix; } /** * Sets the sequence of characters to be used when determining the string * representation of this {@code StringJoiner} and no elements have been * added yet, i.e. when it is empty. A copy of the {@code emptyValue} * parameter is made for this purpose. Note that once an add method has been * called, the {@code StringJoiner} is no longer considered empty, even if * the element(s) added correspond to the empty {@code String}. * * @param emptyValue the characters to return as the value of an empty * {@code StringJoiner} * @return this {@code StringJoiner} itself so the calls may be chained * @throws NullPointerException when the {@code emptyValue} parameter is * {@code null} */ public StringJoiner setEmptyValue(CharSequence emptyValue) { this.emptyValue = Objects.requireNonNull(emptyValue, "The empty value must not be null").toString(); return this; } /** * Returns the current value, consisting of the {@code prefix}, the values * added so far separated by the {@code delimiter}, and the {@code suffix}, * unless no elements have been added in which case, the * {@code prefix + suffix} or the {@code emptyValue} characters are returned * * @return the string representation of this {@code StringJoiner} */ @Override public String toString() { if (value == null) { return emptyValue; } else { if (suffix.equals("")) { return value.toString(); } else { int initialLength = value.length(); String result = value.append(suffix).toString(); // reset value to pre-append initialLength value.setLength(initialLength); return result; } } } /** * Adds a copy of the given {@code CharSequence} value as the next * element of the {@code StringJoiner} value. If {@code newElement} is * {@code null}, then {@code "null"} is added. * * @param newElement The element to add * @return a reference to this {@code StringJoiner} */ public StringJoiner add(CharSequence newElement) { prepareBuilder().append(newElement); return this; } /** * Adds the contents of the given {@code StringJoiner} without prefix and * suffix as the next element if it is non-empty. If the given {@code * StringJoiner} is empty, the call has no effect. * *

A {@code StringJoiner} is empty if {@link #add(CharSequence) add()} * has never been called, and if {@code merge()} has never been called * with a non-empty {@code StringJoiner} argument. * *

If the other {@code StringJoiner} is using a different delimiter, * then elements from the other {@code StringJoiner} are concatenated with * that delimiter and the result is appended to this {@code StringJoiner} * as a single element. * * @param other The {@code StringJoiner} whose contents should be merged * into this one * @throws NullPointerException if the other {@code StringJoiner} is null */ public StringJoiner merge(StringJoiner other) { Objects.requireNonNull(other); if (other.value != null) { StringBuilder builder = prepareBuilder(); StringBuilder otherBuilder = other.value; if (other.prefix.length() < otherBuilder.length()) { builder.append(otherBuilder, other.prefix.length(), otherBuilder.length()); } } return this; } private StringBuilder prepareBuilder() { if (value != null) { value.append(delimiter); } else { value = new StringBuilder().append(prefix); } return value; } /** * The length of the {@code StringJoiner} value, i.e. the length of * {@code String} representation of the {@code StringJoiner}. Note that if * no add methods have been called, then the length of the {@code String} * representation (either {@code prefix + suffix} or {@code emptyValue}) * will be returned. The value should be equivalent to * {@code toString().length()}. * * @return the length of the current value of {@code StringJoiner} */ public int length() { // Remember that we never actually append the suffix unless we return // the full (present) value or some sub-string or length of it, so that // we can add on more if we need to. return (value != null ? value.length() + suffix.length() : emptyValue.length()); } }