View Javadoc
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     &circ;       &circ;
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 }