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