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 */
25package java.util;
26
27import jdk.internal.misc.JavaLangAccess;
28import jdk.internal.misc.SharedSecrets;
29
30/**
31 * {@code StringJoiner} is used to construct a sequence of characters separated
32 * by a delimiter and optionally starting with a supplied prefix
33 * and ending with a supplied suffix.
34 * <p>
35 * Prior to adding something to the {@code StringJoiner}, its
36 * {@code sj.toString()} method will, by default, return {@code prefix + suffix}.
37 * However, if the {@code setEmptyValue} method is called, the {@code emptyValue}
38 * supplied will be returned instead. This can be used, for example, when
39 * creating a string using set notation to indicate an empty set, i.e.
40 * <code>"{}"</code>, where the {@code prefix} is <code>"{"</code>, the
41 * {@code suffix} is <code>"}"</code> and nothing has been added to the
42 * {@code StringJoiner}.
43 *
44 * @apiNote
45 * <p>The String {@code "[George:Sally:Fred]"} may be constructed as follows:
46 *
47 * <pre> {@code
48 * StringJoiner sj = new StringJoiner(":", "[", "]");
49 * sj.add("George").add("Sally").add("Fred");
50 * String desiredString = sj.toString();
51 * }</pre>
52 * <p>
53 * A {@code StringJoiner} may be employed to create formatted output from a
54 * {@link java.util.stream.Stream} using
55 * {@link java.util.stream.Collectors#joining(CharSequence)}. For example:
56 *
57 * <pre> {@code
58 * List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
59 * String commaSeparatedNumbers = numbers.stream()
60 *     .map(i -> i.toString())
61 *     .collect(Collectors.joining(", "));
62 * }</pre>
63 *
64 * @see java.util.stream.Collectors#joining(CharSequence)
65 * @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence)
66 * @since  1.8
67*/
68public final class StringJoiner {
69    private final String prefix;
70    private final String delimiter;
71    private final String suffix;
72
73    /** Contains all the string components added so far. */
74    private String[] elts;
75
76    /** The number of string components added so far. */
77    private int size;
78
79    /** Total length in chars so far, excluding prefix and suffix. */
80    private int len;
81
82    /**
83     * When overriden by the user to be non-null via {@link setEmptyValue}, the
84     * string returned by toString() when no elements have yet been added.
85     * When null, prefix + suffix is used as the empty value.
86     */
87    private String emptyValue;
88
89    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
90
91    /**
92     * Constructs a {@code StringJoiner} with no characters in it, with no
93     * {@code prefix} or {@code suffix}, and a copy of the supplied
94     * {@code delimiter}.
95     * If no characters are added to the {@code StringJoiner} and methods
96     * accessing the value of it are invoked, it will not return a
97     * {@code prefix} or {@code suffix} (or properties thereof) in the result,
98     * unless {@code setEmptyValue} has first been called.
99     *
100     * @param  delimiter the sequence of characters to be used between each
101     *         element added to the {@code StringJoiner} value
102     * @throws NullPointerException if {@code delimiter} is {@code null}
103     */
104    public StringJoiner(CharSequence delimiter) {
105        this(delimiter, "", "");
106    }
107
108    /**
109     * Constructs a {@code StringJoiner} with no characters in it using copies
110     * of the supplied {@code prefix}, {@code delimiter} and {@code suffix}.
111     * If no characters are added to the {@code StringJoiner} and methods
112     * accessing the string value of it are invoked, it will return the
113     * {@code prefix + suffix} (or properties thereof) in the result, unless
114     * {@code setEmptyValue} has first been called.
115     *
116     * @param  delimiter the sequence of characters to be used between each
117     *         element added to the {@code StringJoiner}
118     * @param  prefix the sequence of characters to be used at the beginning
119     * @param  suffix the sequence of characters to be used at the end
120     * @throws NullPointerException if {@code prefix}, {@code delimiter}, or
121     *         {@code suffix} is {@code null}
122     */
123    public StringJoiner(CharSequence delimiter,
124                        CharSequence prefix,
125                        CharSequence suffix) {
126        Objects.requireNonNull(prefix, "The prefix must not be null");
127        Objects.requireNonNull(delimiter, "The delimiter must not be null");
128        Objects.requireNonNull(suffix, "The suffix must not be null");
129        // make defensive copies of arguments
130        this.prefix = prefix.toString();
131        this.delimiter = delimiter.toString();
132        this.suffix = suffix.toString();
133    }
134
135    /**
136     * Sets the sequence of characters to be used when determining the string
137     * representation of this {@code StringJoiner} and no elements have been
138     * added yet, that is, when it is empty.  A copy of the {@code emptyValue}
139     * parameter is made for this purpose. Note that once an add method has been
140     * called, the {@code StringJoiner} is no longer considered empty, even if
141     * the element(s) added correspond to the empty {@code String}.
142     *
143     * @param  emptyValue the characters to return as the value of an empty
144     *         {@code StringJoiner}
145     * @return this {@code StringJoiner} itself so the calls may be chained
146     * @throws NullPointerException when the {@code emptyValue} parameter is
147     *         {@code null}
148     */
149    public StringJoiner setEmptyValue(CharSequence emptyValue) {
150        this.emptyValue = Objects.requireNonNull(emptyValue,
151            "The empty value must not be null").toString();
152        return this;
153    }
154
155    private static int getChars(String s, char[] chars, int start) {
156        int len = s.length();
157        s.getChars(0, len, chars, start);
158        return len;
159    }
160
161    /**
162     * Returns the current value, consisting of the {@code prefix}, the values
163     * added so far separated by the {@code delimiter}, and the {@code suffix},
164     * unless no elements have been added in which case, the
165     * {@code prefix + suffix} or the {@code emptyValue} characters are returned.
166     *
167     * @return the string representation of this {@code StringJoiner}
168     */
169    @Override
170    public String toString() {
171        final String[] elts = this.elts;
172        if (elts == null && emptyValue != null) {
173            return emptyValue;
174        }
175        final int size = this.size;
176        final int addLen = prefix.length() + suffix.length();
177        if (addLen == 0) {
178            compactElts();
179            return size == 0 ? "" : elts[0];
180        }
181        final String delimiter = this.delimiter;
182        final char[] chars = new char[len + addLen];
183        int k = getChars(prefix, chars, 0);
184        if (size > 0) {
185            k += getChars(elts[0], chars, k);
186            for (int i = 1; i < size; i++) {
187                k += getChars(delimiter, chars, k);
188                k += getChars(elts[i], chars, k);
189            }
190        }
191        k += getChars(suffix, chars, k);
192        return jla.newStringUnsafe(chars);
193    }
194
195    /**
196     * Adds a copy of the given {@code CharSequence} value as the next
197     * element of the {@code StringJoiner} value. If {@code newElement} is
198     * {@code null}, then {@code "null"} is added.
199     *
200     * @param  newElement The element to add
201     * @return a reference to this {@code StringJoiner}
202     */
203    public StringJoiner add(CharSequence newElement) {
204        final String elt = String.valueOf(newElement);
205        if (elts == null) {
206            elts = new String[8];
207        } else {
208            if (size == elts.length)
209                elts = Arrays.copyOf(elts, 2 * size);
210            len += delimiter.length();
211        }
212        len += elt.length();
213        elts[size++] = elt;
214        return this;
215    }
216
217    /**
218     * Adds the contents of the given {@code StringJoiner} without prefix and
219     * suffix as the next element if it is non-empty. If the given {@code
220     * StringJoiner} is empty, the call has no effect.
221     *
222     * <p>A {@code StringJoiner} is empty if {@link #add(CharSequence) add()}
223     * has never been called, and if {@code merge()} has never been called
224     * with a non-empty {@code StringJoiner} argument.
225     *
226     * <p>If the other {@code StringJoiner} is using a different delimiter,
227     * then elements from the other {@code StringJoiner} are concatenated with
228     * that delimiter and the result is appended to this {@code StringJoiner}
229     * as a single element.
230     *
231     * @param other The {@code StringJoiner} whose contents should be merged
232     *              into this one
233     * @throws NullPointerException if the other {@code StringJoiner} is null
234     * @return This {@code StringJoiner}
235     */
236    public StringJoiner merge(StringJoiner other) {
237        Objects.requireNonNull(other);
238        if (other.elts == null) {
239            return this;
240        }
241        other.compactElts();
242        return add(other.elts[0]);
243    }
244
245    private void compactElts() {
246        if (size > 1) {
247            final char[] chars = new char[len];
248            int i = 1, k = getChars(elts[0], chars, 0);
249            do {
250                k += getChars(delimiter, chars, k);
251                k += getChars(elts[i], chars, k);
252                elts[i] = null;
253            } while (++i < size);
254            size = 1;
255            elts[0] = jla.newStringUnsafe(chars);
256        }
257    }
258
259    /**
260     * Returns the length of the {@code String} representation
261     * of this {@code StringJoiner}. Note that if
262     * no add methods have been called, then the length of the {@code String}
263     * representation (either {@code prefix + suffix} or {@code emptyValue})
264     * will be returned. The value should be equivalent to
265     * {@code toString().length()}.
266     *
267     * @return the length of the current value of {@code StringJoiner}
268     */
269    public int length() {
270        return (size == 0 && emptyValue != null) ? emptyValue.length() :
271            len + prefix.length() + suffix.length();
272    }
273}
274