1 /*
2 * #%L
3 * JRst :: Api
4 * %%
5 * Copyright (C) 2004 - 2010 CodeLutin
6 * %%
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Lesser Public License for more details.
16 *
17 * You should have received a copy of the GNU General Lesser Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20 * #L%
21 */
22
23 package org.nuiton.jrst.legacy;
24
25 import org.apache.commons.collections.primitives.ArrayCharList;
26 import org.apache.commons.collections.primitives.CharList;
27
28 import java.io.BufferedReader;
29 import java.io.IOException;
30 import java.io.LineNumberReader;
31 import java.io.PushbackReader;
32 import java.io.Reader;
33 import java.util.ArrayList;
34
35 /**
36 * Le principe est d'avoir dans cette classe le moyen de lire, et de retourner
37 * la ou on etait avant la lecture (mark et reset de {@link BufferedReader}).
38 * <p>
39 * Mais il faut aussi pouvoir dire qu'en fin de compte on ne souhaite pas lire
40 * les caracteres que l'on vient de lire ({@link #unread(int)} a peu pres egal a
41 * {@link PushbackReader})
42 * <p>
43 * Le pointer nextChar pointe toujours sur le prochain caractere qui sera lu
44 * <p>
45 * Lorsque l'on appelle la method {@link #mark()} on vide aussi le buffer pour
46 * liberer de la place, car on a plus le moyen de retourner avant le mark que
47 * l'on vient de positionner.
48 * <p>
49 * On contraire du mark de {@link BufferedReader} ou {@link LineNumberReader} il
50 * n'y a pas a specifier le nombre de caractere a garder au maximum, seul la
51 * memoire nous limite. Du coup si l'on utilise cette classe sans mark, au final
52 * on aura dans le buffer tout le contenu du reader, il faut donc utiliser mark
53 * avec cette classe
54 *
55 * buffer
56 *
57 * <pre>
58 * #########################
59 * 0 ˆ ˆ
60 * | |
61 * | + nextChar
62 * + markChar
63 * </pre>
64 *
65 * Created: 27 oct. 06 00:24:57
66 *
67 * @author poussin
68 * @version $Revision$
69 *
70 * Last update: $Date$
71 * by : $Author$
72 */
73 public class AdvancedReader {
74
75 /** le nombre d'espace pour remplacer les tabulations */
76 protected static final String TAB = " ";
77 /** nombre de caractere lu au minimum sur le vrai reader */
78 protected static final int READ_AHEAD = 80;
79
80 protected Reader in;
81 protected CharList buffer;
82
83 protected int charNumber;
84 protected int charNumberMark;
85 protected int lineNumber;
86 protected int lineNumberMark;
87
88 protected int nextChar;
89 protected int markChar;
90
91 protected int readInMark;
92
93 protected boolean nlTwoCtrlChars = false;
94 protected boolean noNlAtEOF = false;
95
96 /**
97 *
98 * @param in the io reader
99 */
100 public AdvancedReader(Reader in) {
101 this.in = new LineNumberReader(in);
102 buffer = new ArrayCharList();
103 }
104
105 public void mark() throws IOException {
106 markChar = nextChar;
107 charNumberMark = charNumber;
108 lineNumberMark = lineNumber;
109
110 free(markChar);
111 }
112
113 public void reset() throws IOException {
114 nextChar = markChar;
115 charNumber = charNumberMark;
116 lineNumber = lineNumberMark;
117
118 }
119
120 public int readSinceMark() {
121 return nextChar - markChar;
122 }
123
124 /**
125 * @return the charNumber
126 */
127 public int getCharNumber() {
128 return charNumber;
129 }
130
131 /**
132 * @return the lineNumber
133 */
134 public int getLineNumber() {
135 return lineNumber;
136 }
137
138 /**
139 * remove number of char in buffer
140 *
141 * @param number
142 * @return the real number of char removed from the head of buffer
143 * @throws IOException
144 */
145 private int free(int number) throws IOException {
146 // fill(number);
147 int result = Math.min(buffer.size(), number);
148 buffer.subList(0, result).clear();
149
150 nextChar -= result;
151 markChar -= result;
152
153 return result;
154 }
155
156 /**
157 * ensure that have number char available and not already read
158 *
159 * @param number ?
160 * @throws IOException
161 */
162 private void fill(int number) throws IOException {
163 int needed = nextChar + number - buffer.size();
164 if (needed > 0) {
165 char[] cbuf = new char[needed + READ_AHEAD];
166 int read = in.read(cbuf);
167 if (read != -1) {
168 for (int i = 0; i < read; i++) {
169 buffer.add(cbuf[i]);
170 }
171 }
172 }
173 }
174
175 public boolean eof() throws IOException {
176 boolean result = -1 == read();
177 if (!result) {
178 unread(1);
179 }
180 return result;
181 }
182
183 public int skip(int number) throws IOException {
184 int result = 0;
185 while (result < number && read() != -1) {
186 result++;
187 }
188 return result;
189 }
190
191 /**
192 * Add a character at the current position
193 * @param character that you want to add
194 */
195 public void add(char character) {
196 buffer.add(nextChar,character);
197 }
198
199
200 /**
201 * go left in reading char buffer
202 *
203 * @param number
204 * @return realy unread char number
205 */
206 public int unread(int number) {
207 int result = Math.min(number, nextChar);
208
209 nextChar -= result;
210 charNumber -= result;
211 for (int i = nextChar; i < nextChar + result; i++) {
212 if (buffer.get(i) == '\n' || (buffer.get(i) == '\r' && i + 1 < nextChar + result && buffer.get(i + 1) != '\n')) {
213 lineNumber--;
214 }
215 }
216
217 return result;
218 }
219
220 /**
221 * Unread the line length
222 *
223 * @param line
224 * line used to know the length to unread
225 * @param addNewLine
226 * if true then add +1 to unread lenght for not present '\n'
227 * @return number of unread char
228 */
229 public int unread(String line, boolean addNewLine) {
230 int result = unread(line.length() + (addNewLine ? (nlTwoCtrlChars ? 2 : 1) : 0));
231 return result;
232 }
233
234 /**
235 * Unread the line length
236 *
237 * @param lines
238 * lines used to know the length to unread
239 * @param addNewLine
240 * if true then add +1 for each line to unread lenght for not
241 * present '\n'
242 * @return number of unread char
243 */
244 public int unread(String[] lines, boolean addNewLine) {
245 int result = 0;
246 for (String line : lines) {
247 result += unread(line, addNewLine);
248 }
249 return result;
250 }
251
252 /**
253 * read one char in buffer
254 *
255 * @return the next char
256 * @throws IOException pour tout pb de lecture
257 */
258 public int read() throws IOException {
259 fill(1);
260 int result = -1;
261 if (nextChar < buffer.size()) {
262 result = buffer.get(nextChar++);
263 charNumber++;
264 if ((char)result == '\n' || ((char)result == '\r' && nextChar < buffer.size() && buffer.get(nextChar) != '\n')) {
265 lineNumber++;
266 }
267 }
268 return result;
269 }
270
271 /**
272 * read one line
273 *
274 * @return one line without '\n' or null if end of file
275 * @throws IOException pour tout pb de lecture
276 */
277 public String readLine() throws IOException {
278 StringBuffer result = new StringBuffer(READ_AHEAD);
279 int c = read();
280 while (c != -1 && c != '\n' && c != '\r') {
281 result.append((char) c);
282 c = read();
283 }
284 nlTwoCtrlChars = false;
285 if (c == '\r') {
286 c = read();
287 if (c != '\n' && c != -1) {
288 unread(1);
289 } else {
290 nlTwoCtrlChars = true;
291 }
292 }
293 noNlAtEOF = false;
294 if (c == -1 && result.length() >= 0) {
295 if (c == -1 && result.length() == 0) {
296 return null;
297 } else {
298 noNlAtEOF = true;
299 return result.toString();
300 }
301 } else {
302 return result.toString();
303 }
304 }
305
306
307
308
309 /**
310 * passe les lignes blanches
311 *
312 * @throws IOException
313 */
314 public void skipBlankLines() throws IOException {
315 readUntil("^\\s*\\S+.*");
316 }
317
318 /**
319 * lit toutes les lignes du fichier
320 *
321 * @return toutes les lignes du fichier
322 * @throws IOException pour tout pb de lecture
323 */
324 String[] readAll() throws IOException {
325 String[] result = readLines(-1);
326 return result;
327 }
328
329 /**
330 * lit un certain nombre de lignes
331 *
332 * @param count
333 * si negatif lit toutes les lignes
334 * @return un certain nombre de lignes
335 * @throws IOException pour tout pb de lecture
336 */
337 public String[] readLines(int count) throws IOException {
338 ArrayList<String> result = new ArrayList<String>();
339
340 String tmp = "";
341 for (int i = count; tmp != null && i != 0; i--) {
342 tmp = readLine();
343 if (tmp != null) {
344 result.add(tmp);
345 }
346 }
347 return result.toArray(new String[result.size()]);
348 }
349
350 /**
351 * lit les lignes jusqu'a la premiere ligne blanche (non retournée)
352 *
353 * @return toutes les ligne jusqu'à la première ligne blanche (non incluse)
354 * @throws IOException pour tout pb de lecture
355 */
356 public String[] readUntilBlank() throws IOException {
357 String[] result = readUntil("\\s*");
358 return result;
359 }
360
361 /**
362 * lit les lignes jusqu'a la ligne qui correspond pas au pattern, cette
363 * ligne n'est pas mise dans le resultat retourne
364 *
365 * @param pattern ?
366 * @return les lignes jusqu'a la ligne qui correspond pas au pattern, cette
367 * ligne n'est pas mise dans le resultat retourne
368 * @throws IOException pour tout pb de lecture
369 */
370 public String[] readUntil(String pattern) throws IOException {
371 ArrayList<String> result = new ArrayList<String>();
372 String tmp = readLine();
373 while (tmp != null && !tmp.matches(pattern)) {
374 result.add(tmp);
375 tmp = readLine();
376 }
377 if (tmp != null) {
378 unread(tmp.length() + (!noNlAtEOF ? (nlTwoCtrlChars ? 2 : 1) : 0)); // +1 for '\n' not in line
379 }
380 return result.toArray(new String[result.size()]);
381 }
382
383 /**
384 * lit les lignes tant que les lignes correspondent au pattern
385 *
386 * @param pattern ?
387 * @return les lignes tant que les lignes correspondent au pattern
388 * @throws IOException pour tout pb de lecture
389 */
390 public String[] readWhile(String pattern) throws IOException {
391 ArrayList<String> result = new ArrayList<String>();
392 String tmp = readLine();
393 while (tmp != null && tmp.matches(pattern)) {
394 result.add(tmp);
395 tmp = readLine();
396 }
397 if (tmp != null) {
398 unread(tmp.length() + (!noNlAtEOF ? (nlTwoCtrlChars ? 2 : 1) : 0)); // +1 for '\n' not in line
399 }
400 return result.toArray(new String[result.size()]);
401 }
402
403 }