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; 787dd252788645e940eada959bdde927426e2531c9Paul Duffin 797dd252788645e940eada959bdde927426e2531c9Paul Duffin default: 807dd252788645e940eada959bdde927426e2531c9Paul Duffin // do nothing 81bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 82bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 83bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor line.append(cbuf, start, off + len - start); 84bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 85bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 86bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** Called when a line is complete. */ 87bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor private boolean finishLine(boolean sawNewline) throws IOException { 880888a09821a98ac0680fad765217302858e70fa4Paul Duffin handleLine(line.toString(), sawReturn 890888a09821a98ac0680fad765217302858e70fa4Paul Duffin ? (sawNewline ? "\r\n" : "\r") 900888a09821a98ac0680fad765217302858e70fa4Paul Duffin : (sawNewline ? "\n" : "")); 91bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor line = new StringBuilder(); 92bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor sawReturn = false; 93bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor return sawNewline; 94bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 95bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 96bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 97bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Subclasses must call this method after finishing character processing, 98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * in order to ensure that any unterminated line in the buffer is 99bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * passed to {@link #handleLine}. 100bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 101bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @throws IOException if an I/O error occurs 102bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 103bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor protected void finish() throws IOException { 104bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor if (sawReturn || line.length() > 0) { 105bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor finishLine(false); 106bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 107bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor } 108bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor 109bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor /** 110bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Called for each line found in the character data passed to 111bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * {@link #add}. 112bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * 113bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @param line a line of text (possibly empty), without any line separators 114bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @param end the line separator; one of {@code "\r"}, {@code "\n"}, 115bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * {@code "\r\n"}, or {@code ""} 116bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @throws IOException if an I/O error occurs 117bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */ 1180888a09821a98ac0680fad765217302858e70fa4Paul Duffin protected abstract void handleLine(String line, String end) 1190888a09821a98ac0680fad765217302858e70fa4Paul Duffin throws IOException; 120bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor} 121