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