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    
017    package com.google.common.base;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.Beta;
022    import com.google.common.annotations.GwtCompatible;
023    
024    import java.io.IOException;
025    import java.util.AbstractList;
026    import java.util.Arrays;
027    import java.util.Iterator;
028    import java.util.Map;
029    import java.util.Map.Entry;
030    
031    import javax.annotation.CheckReturnValue;
032    import 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
066    public 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      @Beta
122      public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
123        checkNotNull(appendable);
124        if (parts.hasNext()) {
125          appendable.append(toString(parts.next()));
126          while (parts.hasNext()) {
127            appendable.append(separator);
128            appendable.append(toString(parts.next()));
129          }
130        }
131        return appendable;
132      }
133    
134      /**
135       * Appends the string representation of each of {@code parts}, using the previously configured
136       * separator between each, to {@code appendable}.
137       */
138      public final <A extends Appendable> A appendTo(A appendable, Object[] parts) throws IOException {
139        return appendTo(appendable, Arrays.asList(parts));
140      }
141    
142      /**
143       * Appends to {@code appendable} the string representation of each of the remaining arguments.
144       */
145      public final <A extends Appendable> A appendTo(
146          A appendable, @Nullable Object first, @Nullable Object second, Object... rest)
147              throws IOException {
148        return appendTo(appendable, iterable(first, second, rest));
149      }
150    
151      /**
152       * <b>Deprecated.</b>
153       *
154       * @since 11.0
155       * @deprecated use {@link #appendTo(StringBuilder, Iterator)} by casting {@code parts} to
156       *     {@code Iterator<?>}, or better yet, by implementing only {@code Iterator} and not
157       *     {@code Iterable}. <b>This method is scheduled for deletion in June 2013.</b>
158       */
159      @Beta
160      @Deprecated
161      public
162      final <I extends Object & Iterable<?> & Iterator<?>> StringBuilder
163          appendTo(StringBuilder builder, I parts) {
164        return appendTo(builder, (Iterator<?>) parts);
165      }
166    
167      /**
168       * Appends the string representation of each of {@code parts}, using the previously configured
169       * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
170       * Iterable)}, except that it does not throw {@link IOException}.
171       */
172      public final StringBuilder appendTo(StringBuilder builder, Iterable<?> parts) {
173        return appendTo(builder, parts.iterator());
174      }
175    
176      /**
177       * Appends the string representation of each of {@code parts}, using the previously configured
178       * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
179       * Iterable)}, except that it does not throw {@link IOException}.
180       *
181       * @since 11.0
182       */
183      @Beta
184      public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
185        try {
186          appendTo((Appendable) builder, parts);
187        } catch (IOException impossible) {
188          throw new AssertionError(impossible);
189        }
190        return builder;
191      }
192    
193      /**
194       * Appends the string representation of each of {@code parts}, using the previously configured
195       * separator between each, to {@code builder}. Identical to {@link #appendTo(Appendable,
196       * Iterable)}, except that it does not throw {@link IOException}.
197       */
198      public final StringBuilder appendTo(StringBuilder builder, Object[] parts) {
199        return appendTo(builder, Arrays.asList(parts));
200      }
201    
202      /**
203       * Appends to {@code builder} the string representation of each of the remaining arguments.
204       * Identical to {@link #appendTo(Appendable, Object, Object, Object...)}, except that it does not
205       * throw {@link IOException}.
206       */
207      public final StringBuilder appendTo(
208          StringBuilder builder, @Nullable Object first, @Nullable Object second, Object... rest) {
209        return appendTo(builder, iterable(first, second, rest));
210      }
211    
212      /**
213       * <b>Deprecated.</b>
214       *
215       * @since 11.0
216       * @deprecated use {@link #join(Iterator)} by casting {@code parts} to
217       *     {@code Iterator<?>}, or better yet, by implementing only {@code Iterator} and not
218       *     {@code Iterable}. <b>This method is scheduled for deletion in June 2013.</b>
219       */
220      @Beta
221      @Deprecated
222      public
223      final <I extends Object & Iterable<?> & Iterator<?>> String join(I parts) {
224        return join((Iterator<?>) parts);
225      }
226    
227      /**
228       * Returns a string containing the string representation of each of {@code parts}, using the
229       * previously configured separator between each.
230       */
231      public final String join(Iterable<?> parts) {
232        return join(parts.iterator());
233      }
234    
235      /**
236       * Returns a string containing the string representation of each of {@code parts}, using the
237       * previously configured separator between each.
238       *
239       * @since 11.0
240       */
241      @Beta
242      public final String join(Iterator<?> parts) {
243        return appendTo(new StringBuilder(), parts).toString();
244      }
245    
246      /**
247       * Returns a string containing the string representation of each of {@code parts}, using the
248       * previously configured separator between each.
249       */
250      public final String join(Object[] parts) {
251        return join(Arrays.asList(parts));
252      }
253    
254      /**
255       * Returns a string containing the string representation of each argument, using the previously
256       * configured separator between each.
257       */
258      public final String join(@Nullable Object first, @Nullable Object second, Object... rest) {
259        return join(iterable(first, second, rest));
260      }
261    
262      /**
263       * Returns a joiner with the same behavior as this one, except automatically substituting {@code
264       * nullText} for any provided null elements.
265       */
266      @CheckReturnValue
267      public Joiner useForNull(final String nullText) {
268        checkNotNull(nullText);
269        return new Joiner(this) {
270          @Override CharSequence toString(Object part) {
271            return (part == null) ? nullText : Joiner.this.toString(part);
272          }
273    
274          @Override public Joiner useForNull(String nullText) {
275            checkNotNull(nullText); // weird: just to satisfy NullPointerTester.
276            throw new UnsupportedOperationException("already specified useForNull");
277          }
278    
279          @Override public Joiner skipNulls() {
280            throw new UnsupportedOperationException("already specified useForNull");
281          }
282        };
283      }
284    
285      /**
286       * Returns a joiner with the same behavior as this joiner, except automatically skipping over any
287       * provided null elements.
288       */
289      @CheckReturnValue
290      public Joiner skipNulls() {
291        return new Joiner(this) {
292          @Override public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts)
293              throws IOException {
294            checkNotNull(appendable, "appendable");
295            checkNotNull(parts, "parts");
296            while (parts.hasNext()) {
297              Object part = parts.next();
298              if (part != null) {
299                appendable.append(Joiner.this.toString(part));
300                break;
301              }
302            }
303            while (parts.hasNext()) {
304              Object part = parts.next();
305              if (part != null) {
306                appendable.append(separator);
307                appendable.append(Joiner.this.toString(part));
308              }
309            }
310            return appendable;
311          }
312    
313          @Override public Joiner useForNull(String nullText) {
314            checkNotNull(nullText); // weird: just to satisfy NullPointerTester.
315            throw new UnsupportedOperationException("already specified skipNulls");
316          }
317    
318          @Override public MapJoiner withKeyValueSeparator(String kvs) {
319            checkNotNull(kvs); // weird: just to satisfy NullPointerTester.
320            throw new UnsupportedOperationException("can't use .skipNulls() with maps");
321          }
322        };
323      }
324    
325      /**
326       * Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
327       * this {@code Joiner} otherwise.
328       */
329      @CheckReturnValue
330      public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
331        return new MapJoiner(this, keyValueSeparator);
332      }
333    
334      /**
335       * An object that joins map entries in the same manner as {@code Joiner} joins iterables and
336       * arrays. Like {@code Joiner}, it is thread-safe and immutable.
337       *
338       * <p>In addition to operating on {@code Map} instances, {@code MapJoiner} can operate on {@code
339       * Multimap} entries in two distinct modes:
340       *
341       * <ul>
342       * <li>To output a separate entry for each key-value pair, pass {@code multimap.entries()} to a
343       *     {@code MapJoiner} method that accepts entries as input, and receive output of the form
344       *     {@code key1=A&key1=B&key2=C}.
345       * <li>To output a single entry for each key, pass {@code multimap.asMap()} to a {@code MapJoiner}
346       *     method that accepts a map as input, and receive output of the form {@code
347       *     key1=[A, B]&key2=C}.
348       * </ul>
349       *
350       * @since 2.0 (imported from Google Collections Library)
351       */
352      public final static class MapJoiner {
353        private final Joiner joiner;
354        private final String keyValueSeparator;
355    
356        private MapJoiner(Joiner joiner, String keyValueSeparator) {
357          this.joiner = joiner; // only "this" is ever passed, so don't checkNotNull
358          this.keyValueSeparator = checkNotNull(keyValueSeparator);
359        }
360    
361        /**
362         * Appends the string representation of each entry of {@code map}, using the previously
363         * configured separator and key-value separator, to {@code appendable}.
364         */
365        public <A extends Appendable> A appendTo(A appendable, Map<?, ?> map) throws IOException {
366          return appendTo(appendable, map.entrySet());
367        }
368    
369        /**
370         * Appends the string representation of each entry of {@code map}, using the previously
371         * configured separator and key-value separator, to {@code builder}. Identical to {@link
372         * #appendTo(Appendable, Map)}, except that it does not throw {@link IOException}.
373         */
374        public StringBuilder appendTo(StringBuilder builder, Map<?, ?> map) {
375          return appendTo(builder, map.entrySet());
376        }
377    
378        /**
379         * Returns a string containing the string representation of each entry of {@code map}, using the
380         * previously configured separator and key-value separator.
381         */
382        public String join(Map<?, ?> map) {
383          return join(map.entrySet());
384        }
385    
386        /**
387         * <b>Deprecated.</b>
388         *
389         * @since 11.0
390         * @deprecated use {@link #appendTo(Appendable, Iterator)} by casting {@code entries} to
391         *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
392         *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
393         *     in June 2013.</b>
394         */
395        @Beta
396        @Deprecated
397        public
398        <A extends Appendable,
399            I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
400            A appendTo(A appendable, I entries) throws IOException {
401          Iterator<? extends Entry<?, ?>> iterator = entries;
402          return appendTo(appendable, iterator);
403        }
404    
405        /**
406         * Appends the string representation of each entry in {@code entries}, using the previously
407         * configured separator and key-value separator, to {@code appendable}.
408         *
409         * @since 10.0
410         */
411        @Beta
412        public <A extends Appendable> A appendTo(A appendable, Iterable<? extends Entry<?, ?>> entries)
413            throws IOException {
414          return appendTo(appendable, entries.iterator());
415        }
416    
417        /**
418         * Appends the string representation of each entry in {@code entries}, using the previously
419         * configured separator and key-value separator, to {@code appendable}.
420         *
421         * @since 11.0
422         */
423        @Beta
424        public <A extends Appendable> A appendTo(A appendable, Iterator<? extends Entry<?, ?>> parts)
425            throws IOException {
426          checkNotNull(appendable);
427          if (parts.hasNext()) {
428            Entry<?, ?> entry = parts.next();
429            appendable.append(joiner.toString(entry.getKey()));
430            appendable.append(keyValueSeparator);
431            appendable.append(joiner.toString(entry.getValue()));
432            while (parts.hasNext()) {
433              appendable.append(joiner.separator);
434              Entry<?, ?> e = parts.next();
435              appendable.append(joiner.toString(e.getKey()));
436              appendable.append(keyValueSeparator);
437              appendable.append(joiner.toString(e.getValue()));
438            }
439          }
440          return appendable;
441        }
442    
443        /**
444         * <b>Deprecated.</b>
445         *
446         * @since 11.0
447         * @deprecated use {@link #appendTo(StringBuilder, Iterator)} by casting {@code entries} to
448         *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
449         *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
450         *     in June 2013.</b>
451         */
452        @Beta
453        @Deprecated
454        public
455        <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
456            StringBuilder appendTo(StringBuilder builder, I entries) throws IOException {
457          Iterator<? extends Entry<?, ?>> iterator = entries;
458          return appendTo(builder, iterator);
459        }
460    
461        /**
462         * Appends the string representation of each entry in {@code entries}, using the previously
463         * configured separator and key-value separator, to {@code builder}. Identical to {@link
464         * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
465         *
466         * @since 10.0
467         */
468        @Beta
469        public StringBuilder appendTo(StringBuilder builder, Iterable<? extends Entry<?, ?>> entries) {
470          return appendTo(builder, entries.iterator());
471        }
472    
473        /**
474         * Appends the string representation of each entry in {@code entries}, using the previously
475         * configured separator and key-value separator, to {@code builder}. Identical to {@link
476         * #appendTo(Appendable, Iterable)}, except that it does not throw {@link IOException}.
477         *
478         * @since 11.0
479         */
480        @Beta
481        public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
482          try {
483            appendTo((Appendable) builder, entries);
484          } catch (IOException impossible) {
485            throw new AssertionError(impossible);
486          }
487          return builder;
488        }
489    
490        /**
491         * <b>Deprecated.</b>
492         *
493         * @since 11.0
494         * @deprecated use {@link #join(Iterator)} by casting {@code entries} to
495         *     {@code Iterator<? extends Entry<?, ?>>}, or better yet, by implementing only
496         *     {@code Iterator} and not {@code Iterable}. <b>This method is scheduled for deletion
497         *     in June 2013.</b>
498         */
499        @Beta
500        @Deprecated
501        public
502        <I extends Object & Iterable<? extends Entry<?, ?>> & Iterator<? extends Entry<?, ?>>>
503            String join(I entries) throws IOException {
504          Iterator<? extends Entry<?, ?>> iterator = entries;
505          return join(iterator);
506        }
507    
508        /**
509         * Returns a string containing the string representation of each entry in {@code entries}, using
510         * the previously configured separator and key-value separator.
511         *
512         * @since 10.0
513         */
514        @Beta
515        public String join(Iterable<? extends Entry<?, ?>> entries) {
516          return join(entries.iterator());
517        }
518    
519        /**
520         * Returns a string containing the string representation of each entry in {@code entries}, using
521         * the previously configured separator and key-value separator.
522         *
523         * @since 11.0
524         */
525        @Beta
526        public String join(Iterator<? extends Entry<?, ?>> entries) {
527          return appendTo(new StringBuilder(), entries).toString();
528        }
529    
530        /**
531         * Returns a map joiner with the same behavior as this one, except automatically substituting
532         * {@code nullText} for any provided null keys or values.
533         */
534        @CheckReturnValue
535        public MapJoiner useForNull(String nullText) {
536          return new MapJoiner(joiner.useForNull(nullText), keyValueSeparator);
537        }
538      }
539    
540      CharSequence toString(Object part) {
541        checkNotNull(part);  // checkNotNull for GWT (do not optimize).
542        return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
543      }
544    
545      private static Iterable<Object> iterable(
546          final Object first, final Object second, final Object[] rest) {
547        checkNotNull(rest);
548        return new AbstractList<Object>() {
549          @Override public int size() {
550            return rest.length + 2;
551          }
552    
553          @Override public Object get(int index) {
554            switch (index) {
555              case 0:
556                return first;
557              case 1:
558                return second;
559              default:
560                return rest[index - 2];
561            }
562          }
563        };
564      }
565    }