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}