001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.base;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023
024import java.io.IOException;
025import java.util.AbstractList;
026import java.util.Arrays;
027import java.util.Iterator;
028import java.util.Map;
029import java.util.Map.Entry;
030
031import javax.annotation.CheckReturnValue;
032import javax.annotation.Nullable;
033
034/**
035 * An object which joins pieces of text (specified as an array, {@link Iterable}, varargs or even a
036 * {@link Map}) with a separator. It either appends the results to an {@link Appendable} or returns
037 * them as a {@link String}. Example: <pre>   {@code
038 *
039 *   Joiner joiner = Joiner.on("; ").skipNulls();
040 *    . . .
041 *   return joiner.join("Harry", null, "Ron", "Hermione");}</pre>
042 *
043 * This returns the string {@code "Harry; Ron; Hermione"}. Note that all input elements are
044 * converted to strings using {@link Object#toString()} before being appended.
045 *
046 * <p>If neither {@link #skipNulls()} nor {@link #useForNull(String)} is specified, the joining
047 * methods will throw {@link NullPointerException} if any given element is null.
048 *
049 * <p><b>Warning: joiner instances are always immutable</b>; a configuration method such as {@code
050 * useForNull} has no effect on the instance it is invoked on! You must store and use the new joiner
051 * instance returned by the method. This makes joiners thread-safe, and safe to store as {@code
052 * static final} constants. <pre>   {@code
053 *
054 *   // Bad! Do not do this!
055 *   Joiner joiner = Joiner.on(',');
056 *   joiner.skipNulls(); // does nothing!
057 *   return joiner.join("wrong", null, "wrong");}</pre>
058 *
059 * <p>See the Guava User Guide article on <a href=
060 * "http://code.google.com/p/guava-libraries/wiki/StringsExplained#Joiner">{@code Joiner}</a>.
061 *
062 * @author Kevin Bourrillion
063 * @since 2.0 (imported from Google Collections Library)
064 */
065@GwtCompatible
066public class Joiner {
067  /**
068   * Returns a joiner which automatically places {@code separator} between consecutive elements.
069   */
070  public static Joiner on(String separator) {
071    return new Joiner(separator);
072  }
073
074  /**
075   * Returns a joiner which automatically places {@code separator} between consecutive elements.
076   */
077  public static Joiner on(char separator) {
078    return new Joiner(String.valueOf(separator));
079  }
080
081  private final String separator;
082
083  private Joiner(String separator) {
084    this.separator = checkNotNull(separator);
085  }
086
087  private Joiner(Joiner prototype) {
088    this.separator = prototype.separator;
089  }
090
091  /**
092   * <b>Deprecated.</b>
093   *
094   * @since 11.0
095   * @deprecated use {@link #appendTo(Appendable, Iterator)} by casting {@code parts} to
096   *     {@code Iterator<?>}, or better yet, by implementing only {@code Iterator} and not
097   *     {@code Iterable}. <b>This method is scheduled for deletion in June 2013.</b>
098   */
099  @Beta
100  @Deprecated
101  public
102  final <A extends Appendable, I extends Object & Iterable<?> & Iterator<?>> A
103      appendTo(A appendable, I parts) throws IOException {
104    return appendTo(appendable, (Iterator<?>) parts);
105  }
106
107  /**
108   * Appends the string representation of each of {@code parts}, using the previously configured
109   * separator between each, to {@code appendable}.
110   */
111  public <A extends Appendable> A appendTo(A appendable, Iterable<?> parts) throws IOException {
112    return appendTo(appendable, parts.iterator());
113  }
114
115  /**
116   * Appends the string representation of each of {@code parts}, using the previously configured
117   * separator between each, to {@code appendable}.
118   *
119   * @since 11.0
120   */
121  public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
122    checkNotNull(appendable);
123    if (parts.hasNext()) {
124      appendable.append(toString(parts.next()));
125      while (parts.hasNext()) {
126        appendable.append(separator);
127        appendable.append(toString(parts.next()));
128      }
129    }
130    return appendable;
131  }
132
133  /**
134   * Appends the string representation of each of {@code parts}, using the previously configured
135   * separator between each, to {@code appendable}.
136   */
137  public final <A extends Appendable> A appendTo(A appendable, Object[] parts) throws IOException {
138    return appendTo(appendable, Arrays.asList(parts));
139  }
140
141  /**
142   * Appends to {@code appendable} the string representation of each of the remaining arguments.
143   */
144  public final <A extends Appendable> A appendTo(
145      A appendable, @Nullable Object first, @Nullable Object second, Object... rest)
146          throws IOException {
147    return appendTo(appendable, iterable(first, second, rest));
148  }
149
150  /**
151   * <b>Deprecated.</b>
152   *
153   * @since 11.0
154   * @deprecated use {@link #appendTo(StringBuilder, Iterator)} by casting {@code parts} to
155   *     {@code Iterator<?>}, or better yet, by implementing only {@code Iterator} and not
156   *     {@code Iterable}. <b>This method is scheduled for deletion in June 2013.</b>
157   */
158  @Beta
159  @Deprecated
160  public
161  final <I extends Object & Iterable<?> & Iterator<?>> StringBuilder
162      appendTo(StringBuilder builder, I parts) {
163    return appendTo(builder, (Iterator<?>) parts);
164  }
165
166  /**
167   * Appends the string representation of each of {@code parts}, using the previously configured
168   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
169   * Iterable)}, except that it does not throw {@link IOException}.
170   */
171  public final StringBuilder appendTo(StringBuilder builder, Iterable<?> parts) {
172    return appendTo(builder, parts.iterator());
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   * @since 11.0
181   */
182  public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
183    try {
184      appendTo((Appendable) builder, parts);
185    } catch (IOException impossible) {
186      throw new AssertionError(impossible);
187    }
188    return builder;
189  }
190
191  /**
192   * Appends the string representation of each of {@code parts}, using the previously configured
193   * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
194   * Iterable)}, except that it does not throw {@link IOException}.
195   */
196  public final StringBuilder appendTo(StringBuilder builder, Object[] parts) {
197    return appendTo(builder, Arrays.asList(parts));
198  }
199
200  /**
201   * Appends to {@code builder} the string representation of each of the remaining arguments.
202   * Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not
203   * throw {@link IOException}.
204   */
205  public final StringBuilder appendTo(
206      StringBuilder builder, @Nullable Object first, @Nullable Object second, Object... rest) {
207    return appendTo(builder, iterable(first, second, rest));
208  }
209
210  /**
211   * <b>Deprecated.</b>
212   *
213   * @since 11.0
214   * @deprecated use {@link #join(Iterator)} by casting {@code parts} to
215   *     {@code Iterator<?>}, or better yet, by implementing only {@code Iterator} and not
216   *     {@code Iterable}. <b>This method is scheduled for deletion in June 2013.</b>
217   */
218  @Beta
219  @Deprecated
220  public
221  final <I extends Object & Iterable<?> & Iterator<?>> String join(I parts) {
222    return join((Iterator<?>) parts);
223  }
224
225  /**
226   * Returns a string containing the string representation of each of {@code parts}, using the
227   * previously configured separator between each.
228   */
229  public final String join(Iterable<?> parts) {
230    return join(parts.iterator());
231  }
232
233  /**
234   * Returns a string containing the string representation of each of {@code parts}, using the
235   * previously configured separator between each.
236   *
237   * @since 11.0
238   */
239  public final String join(Iterator<?> parts) {
240    return appendTo(new StringBuilder(), parts).toString();
241  }
242
243  /**
244   * Returns a string containing the string representation of each of {@code parts}, using the
245   * previously configured separator between each.
246   */
247  public final String join(Object[] parts) {
248    return join(Arrays.asList(parts));
249  }
250
251  /**
252   * Returns a string containing the string representation of each argument, using the previously
253   * configured separator between each.
254   */
255  public final String join(@Nullable Object first, @Nullable Object second, Object... rest) {
256    return join(iterable(first, second, rest));
257  }
258
259  /**
260   * Returns a joiner with the same behavior as this one, except automatically substituting {@code
261   * nullText} for any provided null elements.
262   */
263  @CheckReturnValue
264  public Joiner useForNull(final String nullText) {
265    checkNotNull(nullText);
266    return new Joiner(this) {
267      @Override CharSequence toString(@Nullable Object part) {
268        return (part == null) ? nullText : Joiner.this.toString(part);
269      }
270
271      @Override public Joiner useForNull(String nullText) {
272        checkNotNull(nullText); // weird: just to satisfy NullPointerTester.
273        throw new UnsupportedOperationException("already specified useForNull");
274      }
275
276      @Override public Joiner skipNulls() {
277        throw new UnsupportedOperationException("already specified useForNull");
278      }
279    };
280  }
281
282  /**
283   * Returns a joiner with the same behavior as this joiner, except automatically skipping over any
284   * provided null elements.
285   */
286  @CheckReturnValue
287  public Joiner skipNulls() {
288    return new Joiner(this) {
289      @Override public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts)
290          throws IOException {
291        checkNotNull(appendable, "appendable");
292        checkNotNull(parts, "parts");
293        while (parts.hasNext()) {
294          Object part = parts.next();
295          if (part != null) {
296            appendable.append(Joiner.this.toString(part));
297            break;
298          }
299        }
300        while (parts.hasNext()) {
301          Object part = parts.next();
302          if (part != null) {
303            appendable.append(separator);
304            appendable.append(Joiner.this.toString(part));
305          }
306        }
307        return appendable;
308      }
309
310      @Override public Joiner useForNull(String nullText) {
311        checkNotNull(nullText); // weird: just to satisfy NullPointerTester.
312        throw new UnsupportedOperationException("already specified skipNulls");
313      }
314
315      @Override public MapJoiner withKeyValueSeparator(String kvs) {
316        checkNotNull(kvs); // weird: just to satisfy NullPointerTester.
317        throw new UnsupportedOperationException("can't use .skipNulls() with maps");
318      }
319    };
320  }
321
322  /**
323   * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
324   * this {@code Joiner} otherwise.
325   */
326  @CheckReturnValue
327  public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
328    return new MapJoiner(this, keyValueSeparator);
329  }
330
331  /**
332   * An object that joins map entries in the same manner as {@code Joiner} joins iterables and
333   * arrays. Like {@code Joiner}, it is thread-safe and immutable.
334   *
335   * <p>In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code
336   * Multimap} entries in two distinct modes:
337   *
338   * <ul>
339   * <li>To output a separate entry for each key-value pair, pass {@code multimap.entries()} to a
340   *     {@code MapJoiner} method that accepts entries as input, and receive output of the form
341   *     {@code key1=A&key1=B&key2=C}.
342   * <li>To output a single entry for each key, pass {@code multimap.asMap()} to a {@code MapJoiner}
343   *     method that accepts a map as input, and receive output of the form {@code
344   *     key1=[A, B]&key2=C}.
345   * </ul>
346   *
347   * @since 2.0 (imported from Google Collections Library)
348   */
349  public final static class MapJoiner {
350    private final Joiner joiner;
351    private final String keyValueSeparator;
352
353    private MapJoiner(Joiner joiner, String keyValueSeparator) {
354      this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull
355      this.keyValueSeparator = checkNotNull(keyValueSeparator);
356    }
357
358    /**
359     * Appends the string representation of each entry of {@code map}, using the previously
360     * configured separator and key-value separator, to {@code appendable}.
361     */
362    public <A extends Appendable> A appendTo(A appendable, Map<?, ?> map) throws IOException {
363      return appendTo(appendable, map.entrySet());
364    }
365
366    /**
367     * Appends the string representation of each entry of {@code map}, using the previously
368     * configured separator and key-value separator, to {@code builder}. Identical to {@link
369     * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}.
370     */
371    public StringBuilder appendTo(StringBuilder builder, Map<?, ?> map) {
372      return appendTo(builder, map.entrySet());
373    }
374
375    /**
376     * Returns a string containing the string representation of each entry of {@code map}, using the
377     * previously configured separator and key-value separator.
378     */
379    public String join(Map<?, ?> map) {
380      return join(map.entrySet());
381    }
382
383    /**
384     * <b>Deprecated.</b>
385     *
386     * @since 11.0
387     * @deprecated use {@link #appendTo(Appendable, Iterator)} by casting {@code entries} to
388     *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
389     *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
390     *     in June 2013.</b>
391     */
392    @Beta
393    @Deprecated
394    public
395    <A extends Appendable,
396        I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
397        A appendTo(A appendable, I entries) throws IOException {
398      Iterator<? extends Entry<?, ?>> iterator = entries;
399      return appendTo(appendable, iterator);
400    }
401
402    /**
403     * Appends the string representation of each entry in {@code entries}, using the previously
404     * configured separator and key-value separator, to {@code appendable}.
405     *
406     * @since 10.0
407     */
408    @Beta
409    public <A extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> entries)
410        throws IOException {
411      return appendTo(appendable, entries.iterator());
412    }
413
414    /**
415     * Appends the string representation of each entry in {@code entries}, using the previously
416     * configured separator and key-value separator, to {@code appendable}.
417     *
418     * @since 11.0
419     */
420    @Beta
421    public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
422        throws IOException {
423      checkNotNull(appendable);
424      if (parts.hasNext()) {
425        Entry<?, ?> entry = parts.next();
426        appendable.append(joiner.toString(entry.getKey()));
427        appendable.append(keyValueSeparator);
428        appendable.append(joiner.toString(entry.getValue()));
429        while (parts.hasNext()) {
430          appendable.append(joiner.separator);
431          Entry<?, ?> e = parts.next();
432          appendable.append(joiner.toString(e.getKey()));
433          appendable.append(keyValueSeparator);
434          appendable.append(joiner.toString(e.getValue()));
435        }
436      }
437      return appendable;
438    }
439
440    /**
441     * <b>Deprecated.</b>
442     *
443     * @since 11.0
444     * @deprecated use {@link #appendTo(StringBuilder, Iterator)} by casting {@code entries} to
445     *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
446     *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
447     *     in June 2013.</b>
448     */
449    @Beta
450    @Deprecated
451    public
452    <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
453        StringBuilder appendTo(StringBuilder builder, I entries) throws IOException {
454      Iterator<? extends Entry<?, ?>> iterator = entries;
455      return appendTo(builder, iterator);
456    }
457
458    /**
459     * Appends the string representation of each entry in {@code entries}, using the previously
460     * configured separator and key-value separator, to {@code builder}. Identical to {@link
461     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
462     *
463     * @since 10.0
464     */
465    @Beta
466    public StringBuilder appendTo(StringBuilder builder, Iterable<? extends Entry<?, ?>> entries) {
467      return appendTo(builder, entries.iterator());
468    }
469
470    /**
471     * Appends the string representation of each entry in {@code entries}, using the previously
472     * configured separator and key-value separator, to {@code builder}. Identical to {@link
473     * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
474     *
475     * @since 11.0
476     */
477    @Beta
478    public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
479      try {
480        appendTo((Appendable) builder, entries);
481      } catch (IOException impossible) {
482        throw new AssertionError(impossible);
483      }
484      return builder;
485    }
486
487    /**
488     * <b>Deprecated.</b>
489     *
490     * @since 11.0
491     * @deprecated use {@link #join(Iterator)} by casting {@code entries} to
492     *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
493     *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
494     *     in June 2013.</b>
495     */
496    @Beta
497    @Deprecated
498    public
499    <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
500        String join(I entries) throws IOException {
501      Iterator<? extends Entry<?, ?>> iterator = entries;
502      return join(iterator);
503    }
504
505    /**
506     * Returns a string containing the string representation of each entry in {@code entries}, using
507     * the previously configured separator and key-value separator.
508     *
509     * @since 10.0
510     */
511    @Beta
512    public String join(Iterable<? extends Entry<?, ?>> entries) {
513      return join(entries.iterator());
514    }
515
516    /**
517     * Returns a string containing the string representation of each entry in {@code entries}, using
518     * the previously configured separator and key-value separator.
519     *
520     * @since 11.0
521     */
522    @Beta
523    public String join(Iterator<? extends Entry<?, ?>> entries) {
524      return appendTo(new StringBuilder(), entries).toString();
525    }
526
527    /**
528     * Returns a map joiner with the same behavior as this one, except automatically substituting
529     * {@code nullText} for any provided null keys or values.
530     */
531    @CheckReturnValue
532    public MapJoiner useForNull(String nullText) {
533      return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator);
534    }
535  }
536
537  CharSequence toString(Object part) {
538    checkNotNull(part);  // checkNotNull for GWT (do not optimize).
539    return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
540  }
541
542  private static Iterable<Object> iterable(
543      final Object first, final Object second, final Object[] rest) {
544    checkNotNull(rest);
545    return new AbstractList<Object>() {
546      @Override public int size() {
547        return rest.length + 2;
548      }
549
550      @Override public Object get(int index) {
551        switch (index) {
552          case 0:
553            return first;
554          case 1:
555            return second;
556          default:
557            return rest[index - 2];
558        }
559      }
560    };
561  }
562}