1 /***
2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions are
4 * met :
5 *
6 * . Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 *
9 * . Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * . The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $Id$
29 */
30
31 package palmed.edit.text;
32
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36
37 /***
38 * This class wraps an InputStream in order to detect the line delimiter type.
39 *
40 * @author Mathieu Champlon
41 * @version $Revision$ $Date$
42 */
43 public final class LineDelimiterInspector extends InputStream
44 {
45 /***
46 * The line feed delimiter.
47 */
48 private static final String LF = "\n";
49 /***
50 * The carriage return delimiter.
51 */
52 private static final String CR = "\r";
53 /***
54 * The carriage return/line feed delimiter.
55 */
56 private static final String CR_LF = "\r\n";
57 /***
58 * The wrapped stream.
59 */
60 private final InputStream stream_;
61 /***
62 * The delimiter.
63 */
64 private String delimiter_;
65 /***
66 * Whether the previous read byte was a carriage return or not.
67 */
68 private boolean wasCarriageReturn_;
69 /***
70 * Whether to skip the rest of the stream or not.
71 */
72 private boolean hasLineDelimiterBeenFound_;
73
74 /***
75 * Create a line delimiter inspector.
76 *
77 * @param stream the stream to wrap
78 */
79 public LineDelimiterInspector( final InputStream stream )
80 {
81 if( stream == null )
82 throw new IllegalArgumentException( "parameter 'stream' is null" );
83 stream_ = stream;
84 delimiter_ = CR_LF;
85 wasCarriageReturn_ = false;
86 hasLineDelimiterBeenFound_ = false;
87 }
88
89 /***
90 * Create a default line delimiter.
91 */
92 public LineDelimiterInspector()
93 {
94 stream_ = null;
95 delimiter_ = CR_LF;
96 }
97
98 /***
99 * {@inheritDoc}
100 */
101 public int read() throws IOException
102 {
103 final int b = stream_.read();
104 if( hasLineDelimiterBeenFound_ )
105 return b;
106 return inspect( b );
107 }
108
109 /***
110 * {@inheritDoc}
111 */
112 public int read( final byte[] b ) throws IOException
113 {
114 return read( b, 0, b.length );
115 }
116
117 /***
118 * {@inheritDoc}
119 */
120 public int read( final byte[] b, final int off, final int len ) throws IOException
121 {
122 final int size = stream_.read( b, off, len );
123 for( int i = off; i < size && !hasLineDelimiterBeenFound_; ++i )
124 inspect( b[i] );
125 return size;
126 }
127
128 private int inspect( final int b )
129 {
130 if( b == '\r' )
131 {
132 delimiter_ = CR;
133 wasCarriageReturn_ = true;
134 }
135 else
136 {
137 if( b == '\n' )
138 {
139 if( wasCarriageReturn_ )
140 delimiter_ = CR_LF;
141 else
142 delimiter_ = LF;
143 hasLineDelimiterBeenFound_ = true;
144 }
145 else if( wasCarriageReturn_ )
146 hasLineDelimiterBeenFound_ = true;
147 }
148 return b;
149 }
150
151 /***
152 * Write the delimiter to an output stream.
153 *
154 * @param stream the stream to write into
155 * @throws IOException an io exception occurs
156 */
157 public void write( final OutputStream stream ) throws IOException
158 {
159 stream.write( delimiter_.getBytes() );
160 }
161
162 /***
163 * {@inheritDoc}
164 */
165 public int available() throws IOException
166 {
167 return stream_.available();
168 }
169
170 /***
171 * {@inheritDoc}
172 */
173 public void close() throws IOException
174 {
175 stream_.close();
176 }
177
178 /***
179 * {@inheritDoc}
180 */
181 public void mark( final int readlimit )
182 {
183 stream_.mark( readlimit );
184 }
185
186 /***
187 * {@inheritDoc}
188 */
189 public boolean markSupported()
190 {
191 return stream_.markSupported();
192 }
193
194 /***
195 * {@inheritDoc}
196 */
197 public void reset() throws IOException
198 {
199 stream_.reset();
200 }
201
202 /***
203 * {@inheritDoc}
204 */
205 public long skip( final long n ) throws IOException
206 {
207 return stream_.skip( n );
208 }
209
210 /***
211 * {@inheritDoc}
212 */
213 public String toString()
214 {
215 return delimiter_;
216 }
217 }