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.Map;
027import java.util.Map.Entry;
028import javax.annotation.CheckForNull;
029import org.checkerframework.checker.nullness.qual.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 * <pre>{@code
037 * Joiner joiner = Joiner.on("; ").skipNulls();
038 *  . . .
039 * return joiner.join("Harry", null, "Ron", "Hermione");
040 * }</pre>
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 * <pre>{@code
054 * // Bad! Do not do this!
055 * Joiner joiner = Joiner.on(',');
056 * joiner.skipNulls(); // does nothing!
057 * return joiner.join("wrong", null, "wrong");
058 * }</pre>
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
067@ElementTypesAreNonnullByDefault
068public class Joiner {
069  /** Returns a joiner which automatically places {@code separator} between consecutive elements. */
070  public static Joiner on(String separator) {
071    return new Joiner(separator);
072  }
073
074  /** Returns a joiner which automatically places {@code separator} between consecutive elements. */
075  public static Joiner on(char separator) {
076    return new Joiner(String.valueOf(separator));
077  }
078
079  private final String separator;
080
081  private Joiner(String separator) {
082    this.separator = checkNotNull(separator);
083  }
084
085  private Joiner(Joiner prototype) {
086    this.separator = prototype.separator;
087  }
088
089  /*
090   * In this file, we use <? extends @Nullable Object> instead of <?> to work around a Kotlin bug
091   * (see b/189937072 until we file a bug against Kotlin itself). (The two should be equivalent, so
092   * we normally prefer the shorter one.)
093   */
094
095  /**
096   * Appends the string representation of each of {@code parts}, using the previously configured
097   * separator between each, to {@code appendable}.
098   */
099  @CanIgnoreReturnValue
100  public <A extends Appendable> A appendTo(A appendable, Iterable<? extends @Nullable Object> parts)
101      throws IOException {
102    return appendTo(appendable, parts.iterator());
103  }
104
105  /**
106   * Appends the string representation of each of {@code parts}, using the previously configured
107   * separator between each, to {@code appendable}.
108   *
109   * @since 11.0
110   */
111  @CanIgnoreReturnValue
112  public <A extends Appendable> A appendTo(A appendable, Iterator<? extends @Nullable Object> parts)
113      throws IOException {
114    checkNotNull(appendable);
115    if (parts.hasNext()) {
116      appendable.append(toString(parts.next()));
117      while (parts.hasNext()) {
118        appendable.append(separator);
119        appendable.append(toString(parts.next()));
120      }
121    }
122    return appendable;
123  }
124
125  /**
126   * Appends the string representation of each of {@code parts}, using the previously configured
127   * separator between each, to {@code appendable}.
128   */
129  @CanIgnoreReturnValue
130  public final <A extends Appendable> A appendTo(A appendable, @Nullable Object[] parts)
131      throws IOException {
132    return appendTo(appendable, Arrays.asList(parts));
133  }
134
135  /** Appends to {@code appendable} the string representation of each of the remaining arguments. */
136  @CanIgnoreReturnValue
137  public final <A extends Appendable> A appendTo(
138      A appendable,
139      @CheckForNull Object first,
140      @CheckForNull Object second,
141      @Nullable Object... rest)
142      throws IOException {
143    return appendTo(appendable, iterable(first, second, rest));
144  }
145
146  /**
147   * Appends the string representation of each of {@code parts}, using the previously configured
148   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
149   * Iterable)}, except that it does not throw {@link IOException}.
150   */
151  @CanIgnoreReturnValue
152  public final StringBuilder appendTo(
153      StringBuilder builder, Iterable<? extends @Nullable Object> parts) {
154    return appendTo(builder, parts.iterator());
155  }
156
157  /**
158   * Appends the string representation of each of {@code parts}, using the previously configured
159   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
160   * Iterable)}, except that it does not throw {@link IOException}.
161   *
162   * @since 11.0
163   */
164  @CanIgnoreReturnValue
165  public final StringBuilder appendTo(
166      StringBuilder builder, Iterator<? extends @Nullable Object> parts) {
167    try {
168      appendTo((Appendable) builder, parts);
169    } catch (IOException impossible) {
170      throw new AssertionError(impossible);
171    }
172    return builder;
173  }
174
175  /**
176   * Appends the string representation of each of {@code parts}, using the previously configured
177   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
178   * Iterable)}, except that it does not throw {@link IOException}.
179   */
180  @CanIgnoreReturnValue
181  public final StringBuilder appendTo(StringBuilder builder, @Nullable Object[] parts) {
182    return appendTo(builder, Arrays.asList(parts));
183  }
184
185  /**
186   * Appends to {@code builder} the string representation of each of the remaining arguments.
187   * Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not
188   * throw {@link IOException}.
189   */
190  @CanIgnoreReturnValue
191  public final StringBuilder appendTo(
192      StringBuilder builder,
193      @CheckForNull Object first,
194      @CheckForNull Object second,
195      @Nullable Object... rest) {
196    return appendTo(builder, iterable(first, second, rest));
197  }
198
199  /**
200   * Returns a string containing the string representation of each of {@code parts}, using the
201   * previously configured separator between each.
202   */
203  public final String join(Iterable<? extends @Nullable Object> parts) {
204    return join(parts.iterator());
205  }
206
207  /**
208   * Returns a string containing the string representation of each of {@code parts}, using the
209   * previously configured separator between each.
210   *
211   * @since 11.0
212   */
213  public final String join(Iterator<? extends @Nullable Object> parts) {
214    return appendTo(new StringBuilder(), parts).toString();
215  }
216
217  /**
218   * Returns a string containing the string representation of each of {@code parts}, using the
219   * previously configured separator between each.
220   */
221  public final String join(@Nullable Object[] parts) {
222    return join(Arrays.asList(parts));
223  }
224
225  /**
226   * Returns a string containing the string representation of each argument, using the previously
227   * configured separator between each.
228   */
229  public final String join(
230      @CheckForNull Object first, @CheckForNull Object second, @Nullable Object... rest) {
231    return join(iterable(first, second, rest));
232  }
233
234  /**
235   * Returns a joiner with the same behavior as this one, except automatically substituting {@code
236   * nullText} for any provided null elements.
237   */
238  public Joiner useForNull(String nullText) {
239    checkNotNull(nullText);
240    return new Joiner(this) {
241      @Override
242      CharSequence toString(@CheckForNull Object part) {
243        return (part == null) ? nullText : Joiner.this.toString(part);
244      }
245
246      @Override
247      public Joiner useForNull(String nullText) {
248        throw new UnsupportedOperationException("already specified useForNull");
249      }
250
251      @Override
252      public Joiner skipNulls() {
253        throw new UnsupportedOperationException("already specified useForNull");
254      }
255    };
256  }
257
258  /**
259   * Returns a joiner with the same behavior as this joiner, except automatically skipping over any
260   * provided null elements.
261   */
262  public Joiner skipNulls() {
263    return new Joiner(this) {
264      @Override
265      public <A extends Appendable> A appendTo(
266          A appendable, Iterator<? extends @Nullable Object> parts) throws IOException {
267        checkNotNull(appendable, "appendable");
268        checkNotNull(parts, "parts");
269        while (parts.hasNext()) {
270          Object part = parts.next();
271          if (part != null) {
272            appendable.append(Joiner.this.toString(part));
273            break;
274          }
275        }
276        while (parts.hasNext()) {
277          Object part = parts.next();
278          if (part != null) {
279            appendable.append(separator);
280            appendable.append(Joiner.this.toString(part));
281          }
282        }
283        return appendable;
284      }
285
286      @Override
287      public Joiner useForNull(String nullText) {
288        throw new UnsupportedOperationException("already specified skipNulls");
289      }
290
291      @Override
292      public MapJoiner withKeyValueSeparator(String kvs) {
293        throw new UnsupportedOperationException("can't use .skipNulls() with maps");
294      }
295    };
296  }
297
298  /**
299   * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
300   * this {@code Joiner} otherwise.
301   *
302   * @since 20.0
303   */
304  public MapJoiner withKeyValueSeparator(char keyValueSeparator) {
305    return withKeyValueSeparator(String.valueOf(keyValueSeparator));
306  }
307
308  /**
309   * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
310   * this {@code Joiner} otherwise.
311   */
312  public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
313    return new MapJoiner(this, keyValueSeparator);
314  }
315
316  /**
317   * An object that joins map entries in the same manner as {@code Joiner} joins iterables and
318   * arrays. Like {@code Joiner}, it is thread-safe and immutable.
319   *
320   * <p>In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code
321   * Multimap} entries in two distinct modes:
322   *
323   * <ul>
324   *   <li>To output a separate entry for each key-value pair, pass {@code multimap.entries()} to a
325   *       {@code MapJoiner} method that accepts entries as input, and receive output of the form
326   *       {@code key1=A&key1=B&key2=C}.
327   *   <li>To output a single entry for each key, pass {@code multimap.asMap()} to a {@code
328   *       MapJoiner} method that accepts a map as input, and receive output of the form {@code
329   *       key1=[A, B]&key2=C}.
330   * </ul>
331   *
332   * @since 2.0
333   */
334  public static final class MapJoiner {
335    private final Joiner joiner;
336    private final String keyValueSeparator;
337
338    private MapJoiner(Joiner joiner, String keyValueSeparator) {
339      this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull
340      this.keyValueSeparator = checkNotNull(keyValueSeparator);
341    }
342
343    /**
344     * Appends the string representation of each entry of {@code map}, using the previously
345     * configured separator and key-value separator, to {@code appendable}.
346     */
347    @CanIgnoreReturnValue
348    public <A extends Appendable> A appendTo(A appendable, Map<?, ?> map) throws IOException {
349      return appendTo(appendable, map.entrySet());
350    }
351
352    /**
353     * Appends the string representation of each entry of {@code map}, using the previously
354     * configured separator and key-value separator, to {@code builder}. Identical to {@link
355     * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}.
356     */
357    @CanIgnoreReturnValue
358    public StringBuilder appendTo(StringBuilder builder, Map<?, ?> map) {
359      return appendTo(builder, map.entrySet());
360    }
361
362    /**
363     * Appends the string representation of each entry in {@code entries}, using the previously
364     * configured separator and key-value separator, to {@code appendable}.
365     *
366     * @since 10.0
367     */
368    @CanIgnoreReturnValue
369    public <A extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> entries)
370        throws IOException {
371      return appendTo(appendable, entries.iterator());
372    }
373
374    /**
375     * Appends the string representation of each entry in {@code entries}, using the previously
376     * configured separator and key-value separator, to {@code appendable}.
377     *
378     * @since 11.0
379     */
380    @CanIgnoreReturnValue
381    public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
382        throws IOException {
383      checkNotNull(appendable);
384      if (parts.hasNext()) {
385        Entry<?, ?> entry = parts.next();
386        appendable.append(joiner.toString(entry.getKey()));
387        appendable.append(keyValueSeparator);
388        appendable.append(joiner.toString(entry.getValue()));
389        while (parts.hasNext()) {
390          appendable.append(joiner.separator);
391          Entry<?, ?> e = parts.next();
392          appendable.append(joiner.toString(e.getKey()));
393          appendable.append(keyValueSeparator);
394          appendable.append(joiner.toString(e.getValue()));
395        }
396      }
397      return appendable;
398    }
399
400    /**
401     * Appends the string representation of each entry in {@code entries}, using the previously
402     * configured separator and key-value separator, to {@code builder}. Identical to {@link
403     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
404     *
405     * @since 10.0
406     */
407    @CanIgnoreReturnValue
408    public StringBuilder appendTo(StringBuilder builder, Iterable<? extends Entry<?, ?>> entries) {
409      return appendTo(builder, entries.iterator());
410    }
411
412    /**
413     * Appends the string representation of each entry in {@code entries}, using the previously
414     * configured separator and key-value separator, to {@code builder}. Identical to {@link
415     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
416     *
417     * @since 11.0
418     */
419    @CanIgnoreReturnValue
420    public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
421      try {
422        appendTo((Appendable) builder, entries);
423      } catch (IOException impossible) {
424        throw new AssertionError(impossible);
425      }
426      return builder;
427    }
428
429    /**
430     * Returns a string containing the string representation of each entry of {@code map}, using the
431     * previously configured separator and key-value separator.
432     */
433    public String join(Map<?, ?> map) {
434      return join(map.entrySet());
435    }
436
437    /**
438     * Returns a string containing the string representation of each entry in {@code entries}, using
439     * the previously configured separator and key-value separator.
440     *
441     * @since 10.0
442     */
443    public String join(Iterable<? extends Entry<?, ?>> entries) {
444      return join(entries.iterator());
445    }
446
447    /**
448     * Returns a string containing the string representation of each entry in {@code entries}, using
449     * the previously configured separator and key-value separator.
450     *
451     * @since 11.0
452     */
453    public String join(Iterator<? extends Entry<?, ?>> entries) {
454      return appendTo(new StringBuilder(), entries).toString();
455    }
456
457    /**
458     * Returns a map joiner with the same behavior as this one, except automatically substituting
459     * {@code nullText} for any provided null keys or values.
460     */
461    public MapJoiner useForNull(String nullText) {
462      return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator);
463    }
464  }
465
466  CharSequence toString(@CheckForNull Object part) {
467    /*
468     * requireNonNull is not safe: Joiner.on(...).join(somethingThatContainsNull) will indeed throw.
469     * However, Joiner.on(...).useForNull(...).join(somethingThatContainsNull) *is* safe -- because
470     * it returns a subclass of Joiner that overrides this method to tolerate null inputs.
471     *
472     * Unfortunately, we don't distinguish between these two cases in our public API: Joiner.on(...)
473     * and Joiner.on(...).useForNull(...) both declare the same return type: plain Joiner. To ensure
474     * that users *can* pass null arguments to Joiner, we annotate it as if it always tolerates null
475     * inputs, rather than as if it never tolerates them.
476     *
477     * We rely on checkers to implement special cases to catch dangerous calls to join(), etc. based
478     * on what they know about the particular Joiner instances the calls are performed on.
479     *
480     * (In addition to useForNull, we also offer skipNulls. It, too, tolerates null inputs, but its
481     * tolerance is implemented differently: Its implementation avoids calling this toString(Object)
482     * method in the first place.)
483     */
484    requireNonNull(part);
485    return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
486  }
487
488  private static Iterable<@Nullable Object> iterable(
489      @CheckForNull Object first, @CheckForNull Object second, @Nullable Object[] rest) {
490    checkNotNull(rest);
491    return new AbstractList<@Nullable Object>() {
492      @Override
493      public int size() {
494        return rest.length + 2;
495      }
496
497      @Override
498      @CheckForNull
499      public Object get(int index) {
500        switch (index) {
501          case 0:
502            return first;
503          case 1:
504            return second;
505          default:
506            return rest[index - 2];
507        }
508      }
509    };
510  }
511}