001package org.unix4j.io; 002 003import java.io.IOException; 004import java.io.Reader; 005 006import org.unix4j.line.Line; 007import org.unix4j.line.SingleCharSequenceLine; 008 009/** 010 * Input device based on a {@link Reader} forming the base for most input 011 * devices; handles parsing and recognition of {@link Line lines}. 012 */ 013public class ReaderInput extends AbstractInput { 014 015 private final Reader reader; 016 private final char[] buffer = new char[1024]; 017 private int length; 018 private int offset; 019 020 /** 021 * Constructor with reader. 022 * 023 * @param reader 024 * the reader forming the basis of this input device. 025 */ 026 public ReaderInput(Reader reader) { 027 this.reader = reader; 028 readBuffer(); 029 } 030 031 @Override 032 public boolean hasMoreLines() { 033 return length > offset; 034 } 035 036 @Override 037 public Line readLine() { 038 if (length == 0) { 039 readBuffer(); 040 } 041 if (length > offset) { 042 return makeLine(null); 043 } 044 // no more lines 045 return null; 046 } 047 048 private Line makeLine(StringBuilder lineBuilder) { 049 int len = length; 050 int index = offset; 051 do { 052 while (index < len) { 053 final char ch0 = buffer[index]; 054 if (ch0 == '\n' || ch0 == '\r') { 055 int contentEnd = index; 056 index++; 057 if (index < len) { 058 final char ch1 = buffer[index]; 059 if ((ch1 == '\n' || ch1 == '\r') && ch0 != ch1) { 060 index++; 061 } 062 if (lineBuilder == null) { 063 lineBuilder = new StringBuilder(index - offset); 064 } 065 lineBuilder.append(buffer, offset, index - offset); 066 if (index < len) { 067 offset = index; 068 } else { 069 readBuffer(); 070 } 071 return new SingleCharSequenceLine(lineBuilder, index - contentEnd); 072 } else { 073 if (lineBuilder == null) { 074 lineBuilder = new StringBuilder(len - offset + 1); 075 } 076 lineBuilder.append(buffer, offset, len - offset); 077 return makeLineMaybeWithOneMoreLineEndingChar(lineBuilder); 078 } 079 } 080 index++; 081 } 082 if (lineBuilder == null) { 083 lineBuilder = new StringBuilder(len - offset + 16); 084 } 085 lineBuilder.append(buffer, offset, len - offset); 086 readBuffer(); 087 index = offset; 088 len = length; 089 } while (index < len); 090 091 // eof, no newline, return rest as a line if there is something to 092 // return 093 return lineBuilder.length() > 0 ? new SingleCharSequenceLine(lineBuilder, 0) : null; 094 } 095 096 private Line makeLineMaybeWithOneMoreLineEndingChar(StringBuilder lineBuilder) { 097 int lineEndingLength = 1; 098 readBuffer(); 099 if (offset < length) { 100 final char ch = buffer[offset]; 101 if (ch == '\n' || ch == '\r') { 102 if (lineBuilder.charAt(lineBuilder.length() - 1) != ch) { 103 lineBuilder.append(ch); 104 lineEndingLength++; 105 offset++; 106 } 107 } 108 } 109 return new SingleCharSequenceLine(lineBuilder, lineEndingLength); 110 } 111 112 private void readBuffer() { 113 try { 114 this.length = reader.read(buffer); 115 this.offset = 0; 116 } catch (IOException e) { 117 throw new RuntimeException(e); 118 } 119 } 120 121}