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 }