package com.yoursway.progress.ui.test; import java.io.IOException; import java.util.AbstractList; import java.util.Arrays; import java.util.Iterator; import java.util.List; /** * Join is the only utility for joining pieces of text separated by a delimiter * that you will ever need. It can handle Iterators, Collections, arrays, and * varargs, and can append to any Appendable or just return a String. * <p> * A trivial example: {@code join(":", "a", "b", "c")} gives {@code "a:b:c"}. * See JoinTest for more examples. * <p> * All the methods of this class throw {@link NullPointerException} when a value * of {@code null} is supplied for any parameter. The elements within the * collection, iterator, array, or varargs parameter list <i>may</i> be null -- * these will be represented in the output with the string {@code "null"}. * * @author Kevin Bourrillion */ public final class Join { private Join() { } /** * Appends each of the {@code tokens} to {@code appendable}, separated by * {@code delimiter}. * * @param appendable * the non-null object to append the results to * @param delimiter * a non-null String to append between every element, but not at * the beginning or end * @param tokens * Objects of any type. For each element, if it is an instance of * {@link CharSequence} it will be appended as-is, otherwise it * will be converted to a {@code CharSequence} using * {@link String#valueOf(Object)}. Note that this implies that * null tokens will be appended as the four-character string * {@code "null"}. * @return The same appendable instance that was passed in * @throws JoinException * if an IOException occurs */ public static Appendable join(Appendable appendable, String delimiter, Iterator<?> tokens) { /* This method is the workhorse of the class */ checkNotNull(appendable); checkNotNull(delimiter); checkNotNull(tokens); if (tokens.hasNext()) { try { appendOneToken(appendable, tokens.next()); while (tokens.hasNext()) { appendable.append(delimiter); appendOneToken(appendable, tokens.next()); } } catch (IOException e) { throw new JoinException(e); } } return appendable; } /** * Returns a String containing the {@code tokens}, converted to Strings if * necessary, separated by {@code delimiter}. If {@code tokens} is empty, * returns the empty string. */ public static String join(String delimiter, Iterable<?> tokens) { checkNotNull(tokens); return join(delimiter, tokens.iterator()); } /** * Variant of {@link #join(Appendable,String,Iterator)} where {@code tokens} * is an {@code Iterable}. */ public static Appendable join(Appendable appendable, String delimiter, Iterable<?> tokens) { checkNotNull(tokens); return join(appendable, delimiter, tokens.iterator()); } /** * Variant of {@link #join(Appendable,String,Iterator)} where {@code tokens} * is an array. */ public static Appendable join(Appendable appendable, String delimiter, Object[] tokens) { checkNotNull(tokens); return join(appendable, delimiter, Arrays.asList(tokens)); } /** * Variant of {@link #join(Appendable,String,Iterator)} for tokens given * using varargs. */ public static Appendable join(Appendable appendable, String delimiter, Object firstToken, Object... otherTokens) { checkNotNull(otherTokens); return join(appendable, delimiter, asList(firstToken, otherTokens)); } /** * Variant of {@link #join(String,Iterable)} where {@code tokens} is an * {@code Iterator}. */ public static String join(String delimiter, Iterator<?> tokens) { StringBuilder sb = new StringBuilder(); join(sb, delimiter, tokens); return sb.toString(); } /** * Variant of {@link #join(String,Iterable)} where {@code tokens} is an * array. */ public static String join(String delimiter, Object[] tokens) { checkNotNull(tokens); return join(delimiter, Arrays.asList(tokens)); } /** * Variant of {@link #join(String,Iterable)} for tokens given using varargs. */ public static String join(String delimiter, Object firstToken, Object... otherTokens) { checkNotNull(otherTokens); return join(delimiter, asList(firstToken, otherTokens)); } private static void checkNotNull(Object x) { if (x == null) throw new NullPointerException(); } private static void appendOneToken(Appendable appendable, Object token) throws IOException { appendable.append(toCharSequence(token)); } private static CharSequence toCharSequence(Object token) { return (token instanceof CharSequence) ? (CharSequence) token : String.valueOf(token); } /** * Thrown in response to an {@link IOException} from the supplied * {@link Appendable}. This is used because most callers won't want to * worry about catching an IOException. */ public static class JoinException extends RuntimeException { private JoinException(IOException cause) { super(cause); } private static final long serialVersionUID = 1L; } /** * Duplicate of * {@link com.google.common.collect.Lists#asList(Object, Object[])}. copied * here to remove dependencies. */ private static List<Object> asList(final Object first, final Object[] rest) { return new AbstractList<Object>() { @Override public int size() { return rest.length + 1; } @Override public Object get(int index) { return (index == 0) ? first : rest[index - 1]; } }; } }