001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.base;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018import static java.util.Objects.requireNonNull;
019
020import com.google.common.annotations.GwtCompatible;
021import com.google.errorprone.annotations.CanIgnoreReturnValue;
022import java.io.IOException;
023import java.util.AbstractList;
024import java.util.Arrays;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Map;
028import java.util.Map.Entry;
029import org.jspecify.annotations.Nullable;
030
031/**
032 * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a
033 * {@link Map}) with a separator. It either appends the results to an {@link Appendable} or returns
034 * them as a {@link String}. Example:
035 *
036 * {@snippet :
037 * Joiner joiner = Joiner.on("; ").skipNulls();
038 *  . . .
039 * return joiner.join("Harry", null, "Ron", "Hermione");
040 * }
041 *
042 * <p>This returns the string {@code "Harry; Ron; Hermione"}. Note that all input elements are
043 * converted to strings using {@link Object#toString()} before being appended.
044 *
045 * <p>If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, the joining
046 * methods will throw {@link NullPointerException} if any given element is null.
047 *
048 * <p><b>Warning: joiner instances are always immutable</b>; a configuration method such as {@code
049 * useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner
050 * instance returned by the method. This makes joiners thread-safe, and safe to store as {@code
051 * static final} constants.
052 *
053 * {@snippet :
054 * // Bad! Do not do this!
055 * Joiner joiner = Joiner.on(',');
056 * joiner.skipNulls(); // does nothing!
057 * return joiner.join("wrong", null, "wrong");
058 * }
059 *
060 * <p>See the Guava User Guide article on <a
061 * href="https://github.com/google/guava/wiki/StringsExplained#joiner">{@code Joiner}</a>.
062 *
063 * @author Kevin Bourrillion
064 * @since 2.0
065 */
066@GwtCompatible
067public class Joiner {
068  /** Returns a joiner which automatically places {@code separator} between consecutive elements. */
069  public static Joiner on(String separator) {
070    return new Joiner(separator);
071  }
072
073  /** Returns a joiner which automatically places {@code separator} between consecutive elements. */
074  public static Joiner on(char separator) {
075    return new Joiner(String.valueOf(separator));
076  }
077
078  private final String separator;
079
080  private Joiner(String separator) {
081    this.separator = checkNotNull(separator);
082  }
083
084  private Joiner(Joiner prototype) {
085    this.separator = prototype.separator;
086  }
087
088  /**
089   * Appends the string representation of each of {@code parts}, using the previously configured
090   * separator between each, to {@code appendable}.
091   */
092  @CanIgnoreReturnValue
093  public final <A extends Appendable> A appendTo(A appendable, Iterable<?> parts)
094      throws IOException {
095    return appendTo(appendable, parts.iterator());
096  }
097
098  /**
099   * Appends the string representation of each of {@code parts}, using the previously configured
100   * separator between each, to {@code appendable}.
101   *
102   * @since 11.0
103   */
104  @CanIgnoreReturnValue
105  public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
106    checkNotNull(appendable);
107    if (parts.hasNext()) {
108      appendable.append(toString(parts.next()));
109      while (parts.hasNext()) {
110        appendable.append(separator);
111        appendable.append(toString(parts.next()));
112      }
113    }
114    return appendable;
115  }
116
117  /**
118   * Appends the string representation of each of {@code parts}, using the previously configured
119   * separator between each, to {@code appendable}.
120   */
121  @CanIgnoreReturnValue
122  public final <A extends Appendable> A appendTo(A appendable, @Nullable Object[] parts)
123      throws IOException {
124    @SuppressWarnings("nullness") // TODO(b/316358623): Remove suppression after fixing checker
125    List<?> partsList = Arrays.<@Nullable Object>asList(parts);
126    return appendTo(appendable, partsList);
127  }
128
129  /** Appends to {@code appendable} the string representation of each of the remaining arguments. */
130  @CanIgnoreReturnValue
131  public final <A extends Appendable> A appendTo(
132      A appendable, @Nullable Object first, @Nullable Object second, @Nullable Object... rest)
133      throws IOException {
134    return appendTo(appendable, iterable(first, second, rest));
135  }
136
137  /**
138   * Appends the string representation of each of {@code parts}, using the previously configured
139   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
140   * Iterable)}, except that it does not throw {@link IOException}.
141   */
142  @CanIgnoreReturnValue
143  public final StringBuilder appendTo(StringBuilder builder, Iterable<?> parts) {
144    return appendTo(builder, parts.iterator());
145  }
146
147  /**
148   * Appends the string representation of each of {@code parts}, using the previously configured
149   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
150   * Iterable)}, except that it does not throw {@link IOException}.
151   *
152   * @since 11.0
153   */
154  @CanIgnoreReturnValue
155  public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
156    try {
157      appendTo((Appendable) builder, parts);
158    } catch (IOException impossible) {
159      throw new AssertionError(impossible);
160    }
161    return builder;
162  }
163
164  /**
165   * Appends the string representation of each of {@code parts}, using the previously configured
166   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
167   * Iterable)}, except that it does not throw {@link IOException}.
168   */
169  @CanIgnoreReturnValue
170  public final StringBuilder appendTo(StringBuilder builder, @Nullable Object[] parts) {
171    @SuppressWarnings("nullness") // TODO(b/316358623): Remove suppression after fixing checker
172    List<?> partsList = Arrays.<@Nullable Object>asList(parts);
173    return appendTo(builder, partsList);
174  }
175
176  /**
177   * Appends to {@code builder} the string representation of each of the remaining arguments.
178   * Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not
179   * throw {@link IOException}.
180   */
181  @CanIgnoreReturnValue
182  public final StringBuilder appendTo(
183      StringBuilder builder,
184      @Nullable Object first,
185      @Nullable Object second,
186      @Nullable Object... rest) {
187    return appendTo(builder, iterable(first, second, rest));
188  }
189
190  /**
191   * Returns a string containing the string representation of each of {@code parts}, using the
192   * previously configured separator between each.
193   */
194  public String join(Iterable<?> parts) {
195    /*
196     * If we can quickly determine how many elements there are likely to be, then we can use the
197     * fastest possible implementation, which delegates to the array overload of String.join.
198     *
199     * In theory, we can quickly determine the size of any Collection. However, thanks to
200     * regrettable implementations like our own Sets.filter, Collection.size() is sometimes a
201     * linear-time operation, and it can even have side effects. Thus, we limit the special case to
202     * List, which is _even more likely_ to have size() implemented to be fast and side-effect-free.
203     *
204     * We could consider recognizing specific other collections as safe (like ImmutableCollection,
205     * except ContiguousSet!) or as not worth this optimization (CopyOnWriteArrayList?).
206     */
207    if (parts instanceof List) {
208      int size = ((List<?>) parts).size();
209      if (size == 0) {
210        return "";
211      }
212      CharSequence[] toJoin = new CharSequence[size];
213      int i = 0;
214      for (Object part : parts) {
215        if (i == toJoin.length) {
216          /*
217           * We first initialized toJoin to the size of the input collection. However, that size can
218           * go out of date (for a collection like CopyOnWriteArrayList, which may have been safely
219           * modified concurrently), or it might have been only an estimate to begin with (for a
220           * collection like ConcurrentHashMap, which sums up several counters that may not be in
221           * sync with one another). We accommodate that by resizing as necessary.
222           */
223          toJoin = Arrays.copyOf(toJoin, expandedCapacity(toJoin.length, toJoin.length + 1));
224        }
225        toJoin[i++] = toString(part);
226      }
227      // We might not have seen the expected number of elements, as discussed above.
228      if (i != toJoin.length) {
229        toJoin = Arrays.copyOf(toJoin, i);
230      }
231      return String.join(separator, toJoin);
232    }
233    return join(parts.iterator());
234  }
235
236  /*
237   * TODO(b/381289911): Make the Iterator overload use StringJoiner (including Android or not)—or
238   * some other optimization, given that StringJoiner can over-allocate:
239   * https://bugs.openjdk.org/browse/JDK-8305774
240   */
241
242  // TODO(b/381289911): Optimize MapJoiner similarly to Joiner (including Android or not).
243
244  /**
245   * Returns a string containing the string representation of each of {@code parts}, using the
246   * previously configured separator between each.
247   *
248   * @since 11.0
249   */
250  public final String join(Iterator<?> parts) {
251    return appendTo(new StringBuilder(), parts).toString();
252  }
253
254  /**
255   * Returns a string containing the string representation of each of {@code parts}, using the
256   * previously configured separator between each.
257   */
258  public final String join(@Nullable Object[] parts) {
259    @SuppressWarnings("nullness") // TODO(b/316358623): Remove suppression after fixing checker
260    List<?> partsList = Arrays.<@Nullable Object>asList(parts);
261    return join(partsList);
262  }
263
264  /**
265   * Returns a string containing the string representation of each argument, using the previously
266   * configured separator between each.
267   */
268  public final String join(
269      @Nullable Object first, @Nullable Object second, @Nullable Object... rest) {
270    return join(iterable(first, second, rest));
271  }
272
273  /**
274   * Returns a joiner with the same behavior as this one, except automatically substituting {@code
275   * nullText} for any provided null elements.
276   */
277  public Joiner useForNull(String nullText) {
278    checkNotNull(nullText);
279    return new Joiner(this) {
280      @Override
281      CharSequence toString(@Nullable Object part) {
282        return (part == null) ? nullText : Joiner.this.toString(part);
283      }
284
285      @Override
286      public Joiner useForNull(String nullText) {
287        throw new UnsupportedOperationException("already specified useForNull");
288      }
289
290      @Override
291      public Joiner skipNulls() {
292        throw new UnsupportedOperationException("already specified useForNull");
293      }
294    };
295  }
296
297  /**
298   * Returns a joiner with the same behavior as this joiner, except automatically skipping over any
299   * provided null elements.
300   */
301  public Joiner skipNulls() {
302    return new Joiner(this) {
303      @Override
304      @SuppressWarnings("JoinIterableIterator") // suggests infinite recursion
305      public String join(Iterable<?> parts) {
306        return join(parts.iterator());
307      }
308
309      @Override
310      public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
311        checkNotNull(appendable, "appendable");
312        checkNotNull(parts, "parts");
313        while (parts.hasNext()) {
314          Object part = parts.next();
315          if (part != null) {
316            appendable.append(Joiner.this.toString(part));
317            break;
318          }
319        }
320        while (parts.hasNext()) {
321          Object part = parts.next();
322          if (part != null) {
323            appendable.append(separator);
324            appendable.append(Joiner.this.toString(part));
325          }
326        }
327        return appendable;
328      }
329
330      @Override
331      public Joiner useForNull(String nullText) {
332        throw new UnsupportedOperationException("already specified skipNulls");
333      }
334
335      @Override
336      public MapJoiner withKeyValueSeparator(String kvs) {
337        throw new UnsupportedOperationException("can't use .skipNulls() with maps");
338      }
339    };
340  }
341
342  /**
343   * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
344   * this {@code Joiner} otherwise.
345   *
346   * @since 20.0
347   */
348  public MapJoiner withKeyValueSeparator(char keyValueSeparator) {
349    return withKeyValueSeparator(String.valueOf(keyValueSeparator));
350  }
351
352  /**
353   * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
354   * this {@code Joiner} otherwise.
355   */
356  public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
357    return new MapJoiner(this, keyValueSeparator);
358  }
359
360  /**
361   * An object that joins map entries in the same manner as {@code Joiner} joins iterables and
362   * arrays. Like {@code Joiner}, it is thread-safe and immutable.
363   *
364   * <p>In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code
365   * Multimap} entries in two distinct modes:
366   *
367   * <ul>
368   *   <li>To output a separate entry for each key-value pair, pass {@code multimap.entries()} to a
369   *       {@code MapJoiner} method that accepts entries as input, and receive output of the form
370   *       {@code key1=A&key1=B&key2=C}.
371   *   <li>To output a single entry for each key, pass {@code multimap.asMap()} to a {@code
372   *       MapJoiner} method that accepts a map as input, and receive output of the form {@code
373   *       key1=[A, B]&key2=C}.
374   * </ul>
375   *
376   * @since 2.0
377   */
378  public static final class MapJoiner {
379    private final Joiner joiner;
380    private final String keyValueSeparator;
381
382    private MapJoiner(Joiner joiner, String keyValueSeparator) {
383      this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull
384      this.keyValueSeparator = checkNotNull(keyValueSeparator);
385    }
386
387    /**
388     * Appends the string representation of each entry of {@code map}, using the previously
389     * configured separator and key-value separator, to {@code appendable}.
390     */
391    @CanIgnoreReturnValue
392    public <A extends Appendable> A appendTo(A appendable, Map<?, ?> map) throws IOException {
393      return appendTo(appendable, map.entrySet());
394    }
395
396    /**
397     * Appends the string representation of each entry of {@code map}, using the previously
398     * configured separator and key-value separator, to {@code builder}. Identical to {@link
399     * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}.
400     */
401    @CanIgnoreReturnValue
402    public StringBuilder appendTo(StringBuilder builder, Map<?, ?> map) {
403      return appendTo(builder, map.entrySet());
404    }
405
406    /**
407     * Appends the string representation of each entry in {@code entries}, using the previously
408     * configured separator and key-value separator, to {@code appendable}.
409     *
410     * @since 10.0
411     */
412    @CanIgnoreReturnValue
413    public <A extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> entries)
414        throws IOException {
415      return appendTo(appendable, entries.iterator());
416    }
417
418    /**
419     * Appends the string representation of each entry in {@code entries}, using the previously
420     * configured separator and key-value separator, to {@code appendable}.
421     *
422     * @since 11.0
423     */
424    @CanIgnoreReturnValue
425    public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
426        throws IOException {
427      checkNotNull(appendable);
428      if (parts.hasNext()) {
429        Entry<?, ?> entry = parts.next();
430        appendable.append(joiner.toString(entry.getKey()));
431        appendable.append(keyValueSeparator);
432        appendable.append(joiner.toString(entry.getValue()));
433        while (parts.hasNext()) {
434          appendable.append(joiner.separator);
435          Entry<?, ?> e = parts.next();
436          appendable.append(joiner.toString(e.getKey()));
437          appendable.append(keyValueSeparator);
438          appendable.append(joiner.toString(e.getValue()));
439        }
440      }
441      return appendable;
442    }
443
444    /**
445     * Appends the string representation of each entry in {@code entries}, using the previously
446     * configured separator and key-value separator, to {@code builder}. Identical to {@link
447     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
448     *
449     * @since 10.0
450     */
451    @CanIgnoreReturnValue
452    public StringBuilder appendTo(StringBuilder builder, Iterable<? extends Entry<?, ?>> entries) {
453      return appendTo(builder, entries.iterator());
454    }
455
456    /**
457     * Appends the string representation of each entry in {@code entries}, using the previously
458     * configured separator and key-value separator, to {@code builder}. Identical to {@link
459     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
460     *
461     * @since 11.0
462     */
463    @CanIgnoreReturnValue
464    public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
465      try {
466        appendTo((Appendable) builder, entries);
467      } catch (IOException impossible) {
468        throw new AssertionError(impossible);
469      }
470      return builder;
471    }
472
473    /**
474     * Returns a string containing the string representation of each entry of {@code map}, using the
475     * previously configured separator and key-value separator.
476     */
477    public String join(Map<?, ?> map) {
478      return join(map.entrySet());
479    }
480
481    /**
482     * Returns a string containing the string representation of each entry in {@code entries}, using
483     * the previously configured separator and key-value separator.
484     *
485     * @since 10.0
486     */
487    public String join(Iterable<? extends Entry<?, ?>> entries) {
488      return join(entries.iterator());
489    }
490
491    /**
492     * Returns a string containing the string representation of each entry in {@code entries}, using
493     * the previously configured separator and key-value separator.
494     *
495     * @since 11.0
496     */
497    public String join(Iterator<? extends Entry<?, ?>> entries) {
498      return appendTo(new StringBuilder(), entries).toString();
499    }
500
501    /**
502     * Returns a map joiner with the same behavior as this one, except automatically substituting
503     * {@code nullText} for any provided null keys or values.
504     */
505    public MapJoiner useForNull(String nullText) {
506      return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator);
507    }
508  }
509
510  // TODO(cpovirk): Rename to "toCharSequence."
511  CharSequence toString(@Nullable Object part) {
512    /*
513     * requireNonNull is not safe: Joiner.on(...).join(somethingThatContainsNull) will indeed throw.
514     * However, Joiner.on(...).useForNull(...).join(somethingThatContainsNull) *is* safe -- because
515     * it returns a subclass of Joiner that overrides this method to tolerate null inputs.
516     *
517     * Unfortunately, we don't distinguish between these two cases in our public API: Joiner.on(...)
518     * and Joiner.on(...).useForNull(...) both declare the same return type: plain Joiner. To ensure
519     * that users *can* pass null arguments to Joiner, we annotate it as if it always tolerates null
520     * inputs, rather than as if it never tolerates them.
521     *
522     * We rely on checkers to implement special cases to catch dangerous calls to join(), etc. based
523     * on what they know about the particular Joiner instances the calls are performed on.
524     *
525     * (In addition to useForNull, we also offer skipNulls. It, too, tolerates null inputs, but its
526     * tolerance is implemented differently: Its implementation avoids calling this toString(Object)
527     * method in the first place.)
528     */
529    requireNonNull(part);
530    return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
531  }
532
533  private static Iterable<@Nullable Object> iterable(
534      @Nullable Object first, @Nullable Object second, @Nullable Object[] rest) {
535    checkNotNull(rest);
536    return new AbstractList<@Nullable Object>() {
537      @Override
538      public int size() {
539        return rest.length + 2;
540      }
541
542      @Override
543      public @Nullable Object get(int index) {
544        switch (index) {
545          case 0:
546            return first;
547          case 1:
548            return second;
549          default:
550            return rest[index - 2];
551        }
552      }
553    };
554  }
555
556  // cloned from ImmutableCollection
557  private static int expandedCapacity(int oldCapacity, int minCapacity) {
558    if (minCapacity < 0) {
559      throw new IllegalArgumentException("cannot store more than Integer.MAX_VALUE elements");
560    } else if (minCapacity <= oldCapacity) {
561      return oldCapacity;
562    }
563    // careful of overflow!
564    int newCapacity = oldCapacity + (oldCapacity >> 1) + 1;
565    if (newCapacity < minCapacity) {
566      newCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
567    }
568    if (newCapacity < 0) {
569      newCapacity = Integer.MAX_VALUE;
570      // guaranteed to be >= newCapacity
571    }
572    return newCapacity;
573  }
574}