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      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(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    }