1bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2007 The Guava Authors
3bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
4bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Licensed under the Apache License, Version 2.0 (the "License");
5bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * you may not use this file except in compliance with the License.
6bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * You may obtain a copy of the License at
7bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
8bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * http://www.apache.org/licenses/LICENSE-2.0
9bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
10bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Unless required by applicable law or agreed to in writing, software
11bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * distributed under the License is distributed on an "AS IS" BASIS,
12bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * See the License for the specific language governing permissions and
14bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * limitations under the License.
15bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */
16bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
17bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpackage com.google.common.io;
18bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
19bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.io.IOException;
20bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
21bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/**
22bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Package-protected abstract class that implements the line reading
23bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * algorithm used by {@link LineReader}. Line separators are per {@link
24bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * java.io.BufferedReader}: line feed, carriage return, or carriage
25bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * return followed immediately by a linefeed.
26bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
27bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * <p>Subclasses must implement {@link #handleLine}, call {@link #add}
28bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * to pass character data, and call {@link #finish} at the end of stream.
29bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
30bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @author Chris Nokleberg
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 1.0
32bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */
33bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorabstract class LineBuffer {
34bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /** Holds partial line contents. */
35bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  private StringBuilder line = new StringBuilder();
36bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /** Whether a line ending with a CR is pending processing. */
37bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  private boolean sawReturn;
38bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
39bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
40bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Process additional characters from the stream. When a line separator
41bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * is found the contents of the line and the line separator itself
42bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * are passed to the abstract {@link #handleLine} method.
43bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
44bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @param cbuf the character buffer to process
45bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @param off the offset into the buffer
46bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @param len the number of characters to process
47bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @throws IOException if an I/O error occurs
48bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @see #finish
49bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
50bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected void add(char[] cbuf, int off, int len) throws IOException {
51bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    int pos = off;
52bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    if (sawReturn && len > 0) {
53bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      // Last call to add ended with a CR; we can handle the line now.
54bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      if (finishLine(cbuf[pos] == '\n')) {
55bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        pos++;
56bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
57bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
58bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
59bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    int start = pos;
60bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    for (int end = off + len; pos < end; pos++) {
61bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      switch (cbuf[pos]) {
62bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        case '\r':
63bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          line.append(cbuf, start, pos - start);
64bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          sawReturn = true;
65bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          if (pos + 1 < end) {
66bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            if (finishLine(cbuf[pos + 1] == '\n')) {
67bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor              pos++;
68bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            }
69bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          }
70bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          start = pos + 1;
71bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          break;
72bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
73bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        case '\n':
74bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          line.append(cbuf, start, pos - start);
75bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          finishLine(true);
76bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          start = pos + 1;
77bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          break;
78bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
79bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
80bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    line.append(cbuf, start, off + len - start);
81bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
82bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
83bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /** Called when a line is complete. */
84bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  private boolean finishLine(boolean sawNewline) throws IOException {
85bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    handleLine(line.toString(), sawReturn
86bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        ? (sawNewline ? "\r\n" : "\r")
87bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        : (sawNewline ? "\n" : ""));
88bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    line = new StringBuilder();
89bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    sawReturn = false;
90bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return sawNewline;
91bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
92bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
93bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
94bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Subclasses must call this method after finishing character processing,
95bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * in order to ensure that any unterminated line in the buffer is
96bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * passed to {@link #handleLine}.
97bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @throws IOException if an I/O error occurs
99bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
100bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected void finish() throws IOException {
101bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    if (sawReturn || line.length() > 0) {
102bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      finishLine(false);
103bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
104bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
105bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
106bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
107bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Called for each line found in the character data passed to
108bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * {@link #add}.
109bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
110bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @param line a line of text (possibly empty), without any line separators
111bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @param end the line separator; one of {@code "\r"}, {@code "\n"},
112bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *     {@code "\r\n"}, or {@code ""}
113bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * @throws IOException if an I/O error occurs
114bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
115bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected abstract void handleLine(String line, String end)
116bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      throws IOException;
117bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor}
118