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 }