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