1993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira/*
2993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Copyright (C) 2009 Google Inc.
3993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
4993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
5993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * you may not use this file except in compliance with the License.
6993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * You may obtain a copy of the License at
7993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
8993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * http://www.apache.org/licenses/LICENSE-2.0
9993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
10993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Unless required by applicable law or agreed to in writing, software
11993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
12993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * See the License for the specific language governing permissions and
14993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * limitations under the License.
15993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira */
16993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
171bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedypackage com.google.android.mail.common.base;
18993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
191bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport static com.google.android.mail.common.base.Preconditions.checkArgument;
201bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport static com.google.android.mail.common.base.Preconditions.checkNotNull;
211bdbfefe4b144c7b031a1d9242a0fa061a0ae6b5Scott Kennedyimport static com.google.android.mail.common.base.Preconditions.checkState;
22993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
23993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport com.google.common.base.Joiner;
24993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
25993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.Iterator;
26993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.NoSuchElementException;
27993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.StringTokenizer;
28993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.regex.Matcher;
29993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.regex.Pattern;
30993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereiraimport java.util.regex.PatternSyntaxException;
31993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
32993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira/**
33993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * An object that divides strings (or other instances of {@code CharSequence})
34993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * into substrings, by recognizing a <i>separator</i> (a.k.a. "delimiter")
35993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * which can be expressed as a single character, literal string, regular
36993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * expression, {@code CharMatcher}, or by using a fixed substring length. This
37993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * class provides the complementary functionality to {@link Joiner}.
38993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
39993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * <p>Here is the most basic example of {@code Splitter} usage: <pre>   {@code
40993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
41993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   Splitter.on(',').split("foo,bar")}</pre>
42993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
43993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * This invocation returns an {@code Iterable<String>} containing {@code "foo"}
44993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * and {@code "bar"}, in that order.
45993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
46993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * <p>By default {@code Splitter}'s behavior is very simplistic: <pre>   {@code
47993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
48993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   Splitter.on(',').split("foo,,bar,  quux")}</pre>
49993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
50993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * This returns an iterable containing {@code ["foo", "", "bar", "  quux"]}.
51993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Notice that the splitter does not assume that you want empty strings removed,
52993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * or that you wish to trim whitespace. If you want features like these, simply
53993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * ask for them: <pre> {@code
54993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
55993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   private static final Splitter MY_SPLITTER = Splitter.on(',')
56993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *       .trimResults()
57993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *       .omitEmptyStrings();}</pre>
58993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
59993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * Now {@code MY_SPLITTER.split("foo, ,bar,  quux,")} returns an iterable
60993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * containing just {@code ["foo", "bar", "quux"]}. Note that the order in which
61993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * the configuration methods are called is never significant; for instance,
62993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * trimming is always applied first before checking for an empty result,
63993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * regardless of the order in which the {@link #trimResults()} and
64993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * {@link #omitEmptyStrings()} methods were invoked.
65993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
66993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * <p><b>Warning: splitter instances are always immutable</b>; a configuration
67993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * method such as {@code omitEmptyStrings} has no effect on the instance it
68993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * is invoked on! You must store and use the new splitter instance returned by
69993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * the method. This makes splitters thread-safe, and safe to store as {@code
70993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * static final} constants (as illustrated above). <pre>   {@code
71993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
72993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   // Bad! Do not do this!
73993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   Splitter splitter = Splitter.on('/');
74993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   splitter.trimResults(); // does nothing!
75993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *   return splitter.split("wrong / wrong / wrong");}</pre>
76993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
77993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * The separator recognized by the splitter does not have to be a single
78993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * literal character as in the examples above. See the methods {@link
79993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * #on(String)}, {@link #on(Pattern)} and {@link #on(CharMatcher)} for examples
80993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * of other ways to specify separators.
81993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
82993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * <p><b>Note:</b> this class does not mimic any of the quirky behaviors of
83993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * similar JDK methods; for instance, it does not silently discard trailing
84993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * separators, as does {@link String#split(String)}, nor does it have a default
85993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * behavior of using five particular whitespace characters as separators, like
86993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * {@link StringTokenizer}.
87993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira *
88993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * @author Julien Silland
89993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * @author Jesse Wilson
90993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * @author Kevin Bourrillion
91993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira * @since 2009.09.15 <b>tentative</b>
92993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira */
93993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereirapublic final class Splitter {
94993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private final CharMatcher trimmer;
95993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private final boolean omitEmptyStrings;
96993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private final Strategy strategy;
97993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
98993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private Splitter(Strategy strategy) {
99993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    this(strategy, false, CharMatcher.NONE);
100993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
101993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
102993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private Splitter(Strategy strategy, boolean omitEmptyStrings,
103993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      CharMatcher trimmer) {
104993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    this.strategy = strategy;
105993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    this.omitEmptyStrings = omitEmptyStrings;
106993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    this.trimmer = trimmer;
107993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
108993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
109993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
110993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that uses the given single-character separator. For
111993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * example, {@code Splitter.on(',').split("foo,,bar")} returns an iterable
112993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * containing {@code ["foo", "", "bar"]}.
113993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
114993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param separator the character to recognize as a separator
115993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that recognizes that separator
116993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
117993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter on(char separator) {
118993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return on(CharMatcher.is(separator));
119993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
120993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
121993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
122993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that considers any single character matched by the
123993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * given {@code CharMatcher} to be a separator. For example, {@code
124993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(CharMatcher.anyOf(";,")).split("foo,;bar,quux")} returns an
125993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * iterable containing {@code ["foo", "", "bar", "quux"]}.
126993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
127993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param separatorMatcher a {@link CharMatcher} that determines whether a
128993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     character is a separator
129993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that uses this matcher
130993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
131993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter on(final CharMatcher separatorMatcher) {
132993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkNotNull(separatorMatcher);
133993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
134993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(new Strategy() {
135993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      /*@Override*/ public SplittingIterator iterator(
136993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          Splitter splitter, final CharSequence toSplit) {
137993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return new SplittingIterator(splitter, toSplit) {
138993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override int separatorStart(int start) {
139993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return separatorMatcher.indexIn(toSplit, start);
140993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
141993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
142993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override int separatorEnd(int separatorPosition) {
143993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return separatorPosition + 1;
144993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
145993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        };
146993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
147993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    });
148993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
149993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
150993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
151993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that uses the given fixed string as a separator. For
152993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * example, {@code Splitter.on(", ").split("foo, bar, baz,qux")} returns an
153993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * iterable containing {@code ["foo", "bar", "baz,qux"]}.
154993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
155993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param separator the literal, nonempty string to recognize as a separator
156993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that recognizes that separator
157993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
158993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter on(final String separator) {
159993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkArgument(separator.length() != 0,
160993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        "The separator may not be the empty string.");
161993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
162993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(new Strategy() {
163993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      /*@Override*/ public SplittingIterator iterator(
164993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          Splitter splitter, CharSequence toSplit) {
165993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return new SplittingIterator(splitter, toSplit) {
166993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorStart(int start) {
167993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            int delimeterLength = separator.length();
168993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
169993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            positions:
170993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            for (int p = start, last = toSplit.length() - delimeterLength;
171993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                p <= last; p++) {
172993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira              for (int i = 0; i < delimeterLength; i++) {
173993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                if (toSplit.charAt(i + p) != separator.charAt(i)) {
174993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                  continue positions;
175993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira                }
176993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira              }
177993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira              return p;
178993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            }
179993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return -1;
180993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
181993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
182993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorEnd(int separatorPosition) {
183993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return separatorPosition + separator.length();
184993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
185993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        };
186993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
187993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    });
188993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
189993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
190993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
191993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that considers any subsequence matching {@code
192993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * pattern} to be a separator. For example, {@code
193993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(Pattern.compile("\r?\n")).split(entireFile)} splits a string
194993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * into lines whether it uses DOS-style or UNIX-style line terminators.
195993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
196993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param separatorPattern the pattern that determines whether a subsequence
197993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     is a separator. This pattern may not match the empty string.
198993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that uses this pattern
199993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @throws IllegalArgumentException if {@code separatorPattern} matches the
200993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     empty string
201993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
202993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter on(final Pattern separatorPattern) {
203993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkNotNull(separatorPattern);
204993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkArgument(!separatorPattern.matcher("").matches(),
205993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        "The pattern may not match the empty string: %s", separatorPattern);
206993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
207993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(new Strategy() {
208993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      /*@Override*/ public SplittingIterator iterator(
209993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          final Splitter splitter, CharSequence toSplit) {
210993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        final Matcher matcher = separatorPattern.matcher(toSplit);
211993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return new SplittingIterator(splitter, toSplit) {
212993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorStart(int start) {
213993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return matcher.find(start) ? matcher.start() : -1;
214993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
215993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
216993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorEnd(int separatorPosition) {
217993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return matcher.end();
218993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
219993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        };
220993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
221993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    });
222993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
223993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
224993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
225993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that considers any subsequence matching a given
226993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * pattern (regular expression) to be a separator. For example, {@code
227993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.onPattern("\r?\n").split(entireFile)} splits a string into lines
228993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * whether it uses DOS-style or UNIX-style line terminators. This is
229993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * equivalent to {@code Splitter.on(Pattern.compile(pattern))}.
230993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
231993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param separatorPattern the pattern that determines whether a subsequence
232993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     is a separator. This pattern may not match the empty string.
233993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that uses this pattern
234993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @throws PatternSyntaxException if {@code separatorPattern} is a malformed
235993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     expression
236993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @throws IllegalArgumentException if {@code separatorPattern} matches the
237993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     empty string
238993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
239993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter onPattern(String separatorPattern) {
240993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return on(Pattern.compile(separatorPattern));
241993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
242993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
243993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
244993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that divides strings into pieces of the given length.
245993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * For example, {@code Splitter.atEach(2).split("abcde")} returns an
246993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * iterable containing {@code ["ab", "cd", "e"]}. The last piece can be
247993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * smaller than {@code length} but will never be empty.
248993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
249993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param length the desired length of pieces after splitting
250993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter, with default settings, that can split into fixed sized
251993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     pieces
252993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
253993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public static Splitter fixedLength(final int length) {
254993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkArgument(length > 0, "The length may not be less than 1");
255993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
256993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(new Strategy() {
257993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      /*@Override*/ public SplittingIterator iterator(
258993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          final Splitter splitter, CharSequence toSplit) {
259993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return new SplittingIterator(splitter, toSplit) {
260993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorStart(int start) {
261993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            int nextChunkStart = start + length;
262993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
263993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
264993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
265993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          @Override public int separatorEnd(int separatorPosition) {
266993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira            return separatorPosition;
267993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          }
268993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        };
269993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
270993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    });
271993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
272993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
273993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
274993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that behaves equivalently to {@code this} splitter, but
275993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * automatically omits empty strings from the results. For example, {@code
276993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(',').omitEmptyStrings().split(",a,,,b,c,,")} returns an
277993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * iterable containing only {@code ["a", "b", "c"]}.
278993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
279993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * <p>If either {@code trimResults} option is also specified when creating a
280993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * splitter, that splitter always trims results first before checking for
281993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * emptiness. So, for example, {@code
282993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(':').omitEmptyStrings().trimResults().split(": : : ")} returns
283993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * an empty iterable.
284993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
285993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * <p>Note that it is ordinarily not possible for {@link #split(CharSequence)}
286993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * to return an empty iterable, but when using this option, it can (if the
287993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * input sequence consists of nothing but separators).
288993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
289993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter with the desired configuration
290993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
291993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public Splitter omitEmptyStrings() {
292993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(strategy, true, trimmer);
293993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
294993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
295993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
296993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that behaves equivalently to {@code this} splitter, but
297993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * automatically removes leading and trailing {@linkplain
298993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * CharMatcher#WHITESPACE whitespace} from each returned substring; equivalent
299993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * to {@code trimResults(CharMatcher.WHITESPACE)}. For example, {@code
300993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(',').trimResults().split(" a, b  ,c  ")} returns an iterable
301993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * containing {@code ["a", "b", "c"]}.
302993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
303993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter with the desired configuration
304993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
305993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public Splitter trimResults() {
306993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return trimResults(CharMatcher.WHITESPACE);
307993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
308993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
309993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
310993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Returns a splitter that behaves equivalently to {@code this} splitter, but
311993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * removes all leading or trailing characters matching the given {@code
312993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * CharMatcher} from each returned substring. For example, {@code
313993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splitter.on(',').trimResults(CharMatcher.is('_')).split("_a ,_b_ ,c__")}
314993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * returns an iterable containing {@code ["a ", "b_ ", "c"]}.
315993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
316993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param trimmer a {@link CharMatcher} that determines whether a character
317993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *     should be removed from the beginning/end of a subsequence
318993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return a splitter with the desired configuration
319993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
320993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public Splitter trimResults(CharMatcher trimmer) {
321993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkNotNull(trimmer);
322993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Splitter(strategy, omitEmptyStrings, trimmer);
323993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
324993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
325993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /**
326993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Splits the {@link CharSequence} passed in parameter.
327993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   *
328993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @param sequence the sequence of characters to split
329993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * @return an iteration over the segments split from the parameter.
330993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
331993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  public Iterable<String> split(final CharSequence sequence) {
332993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    checkNotNull(sequence);
333993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
334993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    return new Iterable<String>() {
335993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      /*@Override*/ public Iterator<String> iterator() {
336993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return strategy.iterator(Splitter.this, sequence);
337993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
338993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    };
339993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
340993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
341993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private interface Strategy {
342993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
343993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
344993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
345993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private abstract static class SplittingIterator
346993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      extends AbstractIterator<String> {
347993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    final CharSequence toSplit;
348993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    final CharMatcher trimmer;
349993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    final boolean omitEmptyStrings;
350993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
351993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
352993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Returns the first index in {@code toSplit} at or after {@code start}
353993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * that contains the separator.
354993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
355993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    abstract int separatorStart(int start);
356993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
357993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /**
358993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * Returns the first index in {@code toSplit} after {@code
359993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * separatorPosition} that does not contain a separator. This method is only
360993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     * invoked after a call to {@code separatorStart}.
361993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira     */
362993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    abstract int separatorEnd(int separatorPosition);
363993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
364993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    int offset = 0;
365993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
366993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    protected SplittingIterator(Splitter splitter, CharSequence toSplit) {
367993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.trimmer = splitter.trimmer;
368993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.omitEmptyStrings = splitter.omitEmptyStrings;
369993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      this.toSplit = toSplit;
370993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
371993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
372993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    @Override protected String computeNext() {
373993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      while (offset != -1) {
374993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        int start = offset;
375993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        int end;
376993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
377993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        int separatorPosition = separatorStart(offset);
378993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        if (separatorPosition == -1) {
379993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          end = toSplit.length();
380993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          offset = -1;
381993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        } else {
382993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          end = separatorPosition;
383993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          offset = separatorEnd(separatorPosition);
384993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        }
385993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
386993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        while (start < end && trimmer.matches(toSplit.charAt(start))) {
387993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          start++;
388993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        }
389993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        while (end > start && trimmer.matches(toSplit.charAt(end - 1))) {
390993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          end--;
391993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        }
392993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
393993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        if (omitEmptyStrings && start == end) {
394993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          continue;
395993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        }
396993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
397993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return toSplit.subSequence(start, end).toString();
398993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
399993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return endOfData();
400993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
401993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
402993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
403993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  /*
404993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * Copied from common.collect.AbstractIterator. TODO: un-fork once these
405993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   * packages have been combined into a single library.
406993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira   */
407993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  private static abstract class AbstractIterator<T> implements Iterator<T> {
408993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    State state = State.NOT_READY;
409993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
410993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    enum State {
411993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      READY, NOT_READY, DONE, FAILED,
412993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
413993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
414993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    T next;
415993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
416993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    protected abstract T computeNext();
417993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
418993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    protected final T endOfData() {
419993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      state = State.DONE;
420993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return null;
421993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
422993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
423993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public final boolean hasNext() {
424993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      checkState(state != State.FAILED);
425993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      switch (state) {
426993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        case DONE:
427993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          return false;
428993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        case READY:
429993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira          return true;
430993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        default:
431993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
432993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return tryToComputeNext();
433993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
434993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
435993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    boolean tryToComputeNext() {
436993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      state = State.FAILED; // temporary pessimism
437993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      next = computeNext();
438993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (state != State.DONE) {
439993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        state = State.READY;
440993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        return true;
441993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
442993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return false;
443993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
444993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
445993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    public final T next() {
446993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      if (!hasNext()) {
447993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira        throw new NoSuchElementException();
448993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      }
449993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      state = State.NOT_READY;
450993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      return next;
451993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
452993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira
453993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    /*@Override*/ public void remove() {
454993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira      throw new UnsupportedOperationException();
455993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira    }
456993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira  }
457993ef2674bf860a84c5c17e51a7a9e13e5d56504Mindy Pereira}
458