Clover Coverage Report
Coverage timestamp: Sa Aug 2 2008 13:56:27 CEST
../../../../img/srcFileCovDistChart8.png 47% of files have more coverage
196   664   122   6,76
130   357   0,62   29
29     4,21  
1    
 
  TextInput       Line # 34 196 122 75,2% 0.7521127
 
  (138)
 
1    /* $Id: TextInput.java,v 1.1 2008/07/26 07:55:42 m31 Exp $
2    *
3    * This file is part of the project "Hilbert II" - http://www.qedeq.org
4    *
5    * Copyright 2000-2008, Michael Meyling <mime@qedeq.org>.
6    *
7    * "Hilbert II" is free software; you can redistribute
8    * it and/or modify it under the terms of the GNU General Public
9    * License as published by the Free Software Foundation; either
10    * version 2 of the 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 Public License for more details.
16    */
17   
18    package org.qedeq.base.io;
19   
20    import java.io.File;
21    import java.io.IOException;
22    import java.io.InputStream;
23    import java.io.Reader;
24   
25    import org.qedeq.base.utility.StringUtility;
26   
27   
28    /**
29    * This class provides convenient methods for parsing input.
30    *
31    * @version $Revision: 1.1 $
32    * @author Michael Meyling
33    */
 
34    public class TextInput extends InputStream {
35   
36    /** Char marking end of data. */
37    public static final int EOF = -1;
38   
39    /** Char marking end of input line. */
40    // public final static char CR = '\n'; // LATER mime 20050613: delete if running on all platforms
41    public static final char CR = '\012';
42   
43    /** String for marking current reading position. */
44    private static final String MARKER = "#####";
45   
46    /** Holds the data. */
47    private final StringBuffer source;
48   
49    /** Current line number (starting with 0). */
50    private int lineNumber = 0;
51   
52    /** Current column (starting with 0). */
53    private int column = 0;
54   
55    /** Current reading position (starting with 0). */
56    private int position = 0;
57   
58    /**
59    * Constructor using <code>Reader</code> source.
60    *
61    * @param reader Data source. This reader will be closed (even if reading fails).
62    * @throws IOException Reading failed.
63    * @throws NullPointerException Argument was a null pointer.
64    */
 
65  61691 toggle public TextInput(final Reader reader) throws IOException {
66  61691 try {
67  61691 if (reader == null) {
68  0 throw new NullPointerException(
69    "no null pointer as argument accepted");
70    }
71  61691 this.source = new StringBuffer();
72    // TODO mime 20080307: optimize reading
73  61691 int c;
74  ? while (-1 != (c = reader.read())) {
75    this.source.append((char) c);
76    }
77    } finally {
78  61691 IoUtility.close(reader);
79    }
80    }
81   
82    /**
83    * Constructor using <code>StringBuffer</code> source.
84    *
85    * @param source data source
86    * @throws NullPointerException Argument was a null pointer.
87    */
 
88  96 toggle public TextInput(final StringBuffer source) {
89  96 if (source == null) {
90  1 throw new NullPointerException(
91    "no null pointer as argument accepted");
92    }
93  95 this.source = source;
94    }
95   
96    /**
97    * Constructor using <code>String</code> source.
98    *
99    * @param source data source
100    * @throws NullPointerException Argument was a null pointer.
101    */
 
102  37 toggle public TextInput(final String source) {
103  37 if (source == null) {
104  1 throw new NullPointerException(
105    "no null pointer as argument accepted");
106    }
107  36 this.source = new StringBuffer(source);
108    }
109   
110   
111    /**
112    * Constructor using <code>FILE</code> source.
113    *
114    * @param file Data source.
115    * @param encoding Take this encoding for file.
116    * @throws IOException File reading failed.
117    * @throws NullPointerException One argument was a null pointer.
118    */
 
119  1 toggle public TextInput(final File file, final String encoding) throws IOException {
120  1 if (file == null) {
121  0 throw new NullPointerException(
122    "no null pointer as argument accepted");
123    }
124  1 this.source = new StringBuffer();
125  1 IoUtility.loadFile(file, source, encoding);
126    }
127   
128    /**
129    * Reads a single character and increments the reading position
130    * by one. If no characters are left, <code>-1</code> is returned.
131    * Otherwise a cast to <code>char</code> gives the character read.
132    *
133    * @return Character read, if there are no more chars
134    * <code>-1</code> is returned.
135    */
 
136    toggle public final int read() {
137    if (position >= source.length()) {
138  32 return EOF;
139    }
140    if (getChar() == CR) {
141  169468511 lineNumber++;
142  169468511 column = 0;
143    } else {
144    column++;
145    }
146    return source.charAt(position++);
147    }
148   
149    /**
150    * Decrements the reading position by one and reads a single character.
151    * If no characters are left, <code>-1</code> is returned.
152    * Otherwise a cast to <code>char</code> gives the character read.
153    *
154    * @return Character read, if there are no more chars
155    * <code>-1</code> is returned.
156    */
 
157  558924 toggle public final int readInverse() {
158  558924 if (position <= 0) {
159  1 return -1;
160    }
161  558923 final char c = source.charAt(--position);
162  558923 if (c == CR) {
163  37 lineNumber--;
164  37 int pos = source.lastIndexOf("" + CR, position - 1);
165  37 if (pos < 0) {
166  2 column = position;
167    } else {
168  35 column = position - 1 - pos;
169    }
170    } else {
171  558886 column--;
172  558886 if (column < 0) {
173  0 throw new IllegalStateException("column less then 0");
174    }
175    }
176  558923 return c;
177    }
178   
179    /**
180    * Reads a given amount of characters and increments the reading position
181    * accordingly.
182    *
183    * @param number amount of characters to read
184    * @return string read
185    */
 
186  2 toggle public final String readString(final int number) {
187  2 final StringBuffer result = new StringBuffer(number);
188  12 for (int i = 0; i < number; i++) {
189  10 final int c = read();
190  10 if (c != -1) {
191  10 result.append((char) c);
192    } else {
193  0 break;
194    }
195    }
196  2 return result.toString();
197    }
198   
199    /**
200    * Reads a single character and does not change the reading
201    * position. If no characters are left, <code>-1</code> is returned.
202    * Otherwise a cast to <code>char</code> gives the character read.
203    *
204    * @return Character read at current position, if there are no more chars
205    * <code>-1</code> is returned
206    */
 
207    toggle public final int getChar() {
208    if (position >= source.length()) {
209  440 return -1;
210    }
211    return source.charAt(position);
212    }
213   
214    /**
215    * Reads a single character and does not change the reading
216    * position. If offset addition leads out of the source,
217    * <code>-1</code> is returned. Otherwise a cast to <code>char</code>
218    * gives the character read.
219    *
220    * @param skip Offset from current reading position. Maybe negative.
221    * @return Character read, if position is out of scope
222    * <code>-1</code> is returned.
223    */
 
224  173 toggle public final int getChar(final int skip) {
225  173 if (position + skip < 0 || position + skip >= source.length()) {
226  20 return -1;
227    }
228  153 return source.charAt(position + skip);
229    }
230   
231   
232    /**
233    * Skips white space, beginning from reading position.
234    * Changes reading position to next non white space
235    * character.
236    */
 
237  18421 toggle public final void skipWhiteSpace() {
238  22210 while (!isEmpty() && Character.isWhitespace((char) getChar())) {
239  3789 read();
240    }
241    }
242   
243    /**
244    * Skips white space, beginning from reading position.
245    * Changes reading position to next non white space
246    * character.
247    */
 
248  3 toggle public final void skipWhiteSpaceInverse() {
249  13 while (getPosition() > 0 && Character.isWhitespace((char) getChar(-1))) {
250  10 readInverse();
251    }
252    }
253   
254    /**
255    * Skip current position back to beginning of an XML tag.
256    * This is mainly something like <code>&lt;tagName</code>.
257    *
258    * @throws IllegalArgumentException No begin of XML tag found.
259    */
 
260  32558 toggle public final void skipBackToBeginOfXmlTag() {
261  32558 if ('<' == getChar()) {
262  1 return;
263    }
264  32557 boolean quoted = false;
265  32557 do {
266  558911 if (-1 == readInverse()) {
267  0 throw new IllegalArgumentException("begin of xml tag not found");
268    }
269  558911 if ('\"' == getChar()) {
270  49636 quoted = !quoted;
271    }
272  558911 } while (quoted || '<' != getChar());
273    }
274   
275    /**
276    * Skip current position forward to end of an XML tag.
277    * This is mainly something like <code>&gt;</code>. Quoted data is skipped.
278    *
279    * @throws IllegalArgumentException No en of XML tag found.
280    */
 
281  0 toggle public final void skipForwardToEndOfXmlTag() {
282  0 if ('>' == getChar()) {
283  0 return;
284    }
285  0 boolean quoted = false;
286  0 while ('>' != getChar()) {
287  0 if ('\"' == getChar()) {
288  0 quoted = !quoted;
289    }
290  0 if (!quoted) {
291  0 if (-1 == read()) {
292  0 throw new IllegalArgumentException("end of xml tag not found");
293    }
294    }
295    }
296  0 read(); // skip '>'
297    }
298   
299    /**
300    * Reads tag or attribute name out of XML stream. Whitespace is skipped and
301    * characters are read till &quot;=&quot; or &quot;&gt;&quot; or whitespace is found.
302    *
303    * @return Name of tag or attribute.
304    * @throws IllegalArgumentException Next non white space character is &quot;=&quot;
305    * or &quot;&gt;&quot;.
306    */
 
307  7164 toggle public final String readNextXmlName() {
308  7164 skipWhiteSpace();
309  7164 if (isEmpty() || '=' == getChar() || '>' == getChar()) {
310  0 throw new IllegalArgumentException(
311    "begin of attribute expected");
312    }
313  7164 StringBuffer buffer = new StringBuffer();
314  37348 while (!isEmpty() && '=' != getChar() && '>' != getChar()
315    && !Character.isWhitespace((char) getChar())) {
316  30184 buffer.append((char) read());
317    }
318  7164 return buffer.toString();
319    }
320   
321    /**
322    * Reads attribute value out of XML stream. Whitespace is skipped and an &quot;=&quot;
323    * is expected to follow. Again whitespace is skipped. If no quotation mark follows
324    * characters are read till whitespace or &quot;&gt;&quot; occurs. Otherwise data is
325    * read till an ending quotation mark comes.
326    *
327    * @return Value read.
328    * @throws IllegalArgumentException Following had not one of the following forms:
329    * <pre>
330    * = "value"
331    * </pre>
332    * <pre>
333    * = value
334    * </pre>
335    */
 
336  3745 toggle public final String readNextAttributeValue() {
337  3745 skipWhiteSpace();
338  3745 if (isEmpty() || '=' != getChar()) {
339  0 throw new IllegalArgumentException(
340    "\"=\" expected");
341    }
342  3745 read(); // read =
343  3745 skipWhiteSpace();
344  3745 if (isEmpty() || '>' == getChar()) {
345  0 throw new IllegalArgumentException(
346    "attribute value expected");
347    }
348  3745 StringBuffer buffer = new StringBuffer();
349  3745 if ('\"' == getChar()) {
350  3745 read(); // read "
351  29548 while (!isEmpty() && '\"' != getChar()) {
352  25803 buffer.append((char) read());
353    }
354  3745 if ('\"' != getChar()) {
355  0 throw new IllegalArgumentException("\" expected");
356    }
357  3745 read(); // read "
358    } else {
359  0 while (!isEmpty() && '>' != getChar()
360    && !Character.isWhitespace((char) getChar())) {
361  0 buffer.append((char) read());
362    }
363    }
364  3745 return StringUtility.decodeXmlMarkup(buffer);
365    }
366   
367    /**
368    * Is there no data left for reading?
369    *
370    * @return is all data read?
371    */
 
372  128759 toggle public final boolean isEmpty() {
373  128759 return position >= source.length();
374    }
375   
376    /**
377    * Is there no data left for reading after skipping?
378    *
379    * @param skip Add this number to current position.
380    * @return Is data empty at that new position?
381    */
 
382  8 toggle public final boolean isEmpty(final int skip) {
383  8 return position + skip >= source.length();
384    }
385   
386    /**
387    * Reads the next string containing only letters or digits,
388    * leading whitespace is skipped.
389    * Changes reading position.
390    *
391    * @return read string
392    * @throws IllegalArgumentException if no such characters could
393    * be found
394    */
 
395  6 toggle public final String readLetterDigitString() {
396  6 skipWhiteSpace();
397  6 if (isEmpty() || !Character.isLetterOrDigit((char) getChar())) {
398  3 read(); // for showing correct position
399  3 throw new IllegalArgumentException(
400    "letter or digit expected");
401    }
402  3 StringBuffer buffer = new StringBuffer();
403  22 while (!isEmpty() && Character.isLetterOrDigit((char) getChar())) {
404  19 buffer.append((char) read());
405    }
406  3 return buffer.toString();
407    }
408   
409   
410    /**
411    * Reads the next (big) integer, leading whitespace is skipped.
412    * The first character might be a minus sign, the rest must be
413    * digits. Leading zero digits are not allowed, also "-0" is not
414    * accepted. <p>
415    * Changes reading position.
416    *
417    * @return read integer
418    * @throws IllegalArgumentException if no digits where found or
419    * the number was to big for an <code>int</code>
420    */
 
421  5 toggle public final String readCounter() {
422  5 skipWhiteSpace();
423  5 if (isEmpty()) {
424  0 throw new IllegalArgumentException("integer expected");
425    }
426  5 StringBuffer buffer = new StringBuffer();
427  5 if (getChar() == '-') {
428  0 buffer.append(read());
429    }
430  5 final int begin = getPosition();
431  5 if (!Character.isDigit((char) getChar())) {
432  2 throw new IllegalArgumentException("digit expected");
433    }
434  16 while (!isEmpty() && Character.isDigit((char) getChar())) {
435  13 buffer.append((char) read());
436    }
437  3 if (buffer.length() >= 2 && ('0' == buffer.charAt(0)
438    || '-' == buffer.charAt(0) && '0' == buffer.charAt(1))) {
439  1 setPosition(begin); // for showing correct position
440  1 throw new IllegalArgumentException("no leading zeros allowed");
441    }
442  2 return buffer.toString();
443    }
444   
445    /**
446    * Reads the next quoted string, leading whitespace is skipped.
447    * A correctly quoted string could be created by adding a leading and
448    * a trailing quote character and doubling each other quote character.
449    * The resulting string is dequoted.
450    * Changes reading position.
451    *
452    * @return Dequoted string read.
453    * @throws IllegalArgumentException No correctly quoted string was found.
454    */
 
455  5 toggle public final String readQuoted() {
456  5 skipWhiteSpace();
457  5 if (isEmpty() || read() != '\"') {
458  1 throw new IllegalArgumentException(
459    "\" expected");
460    }
461  4 StringBuffer unquoted = new StringBuffer();
462  4 char c;
463  4 do {
464  14 if (isEmpty()) {
465  0 throw new IllegalArgumentException(
466    "ending \" expected");
467    }
468  14 c = (char) read();
469  14 if (c != '\"') {
470  9 unquoted.append(c);
471    } else { // c == '\"'
472  5 if (isEmpty() || getChar() != '\"') {
473  4 break; // success
474    }
475  1 unquoted.append((char) read());
476    }
477    } while (true);
478  4 return unquoted.toString();
479    }
480   
481    /**
482    * Returns the current line number.
483    *
484    * @return Current line number (starting with line 1).
485    */
 
486    toggle public final int getRow() {
487    return lineNumber + 1;
488    }
489   
490    /**
491    * Returns the current column number.
492    *
493    * @return Current column number (starting with line 1).
494    */
 
495  2181888 toggle public final int getColumn() {
496  2181888 return column + 1;
497    }
498   
499    /**
500    * Returns the current line.
501    *
502    * @return Current line.
503    */
 
504  6 toggle public final String getLine() {
505  6 int min = position - 1;
506  6 while (min >= 0 && source.charAt(min) != CR) {
507  0 min--;
508    }
509  6 int max = position;
510  330 while (max < source.length()
511    && source.charAt(max) != CR) {
512  324 max++;
513    }
514  6 if (min + 1 >= max) {
515  0 return "";
516    }
517  6 return source.substring(min + 1, max);
518    }
519   
520    /**
521    * Returns the current position. Starting with 0.
522    *
523    * @return Current position.
524    */
 
525  86874 toggle public final int getPosition() {
526  86874 return position;
527    }
528   
529    /**
530    * Returns the highest position number possible. This is equal
531    * to the length of the source.
532    *
533    * @return Maximum position.
534    */
 
535  5 toggle public final int getMaximumPosition() {
536  5 return source.length();
537    }
538   
539    /**
540    * Sets the current position (and indirectly the line number).
541    *
542    * @param position Set current position to this value.
543    */
 
544  9251 toggle public final void setPosition(final int position) {
545  9251 if (position >= source.length()) {
546  480 this.position = source.length();
547  8771 } else if (this.position != position) {
548  6341 this.position = 0;
549  6341 this.lineNumber = 0;
550  6341 this.column = 0;
551  2650893 for (int i = 0; i < position; i++) { // Q & D
552  2644552 read();
553    }
554    }
555    }
556   
557    /**
558    * Sets the current line number (and indirectly the position).
559    *
560    * @param row Move to this line number.
561    */
 
562  61701 toggle public final void setRow(final int row) {
563  61701 int r = row;
564    // check if row is under lower bound
565  61701 if (r <= 0) {
566  0 r = 1;
567    }
568    // check if already at wanted position
569  61701 if (getRow() == r) {
570  2 return;
571    }
572    // check if already at end of file
573  61699 if (getPosition() >= source.length() && getRow() >= r) {
574  0 return;
575    }
576  61699 if (getRow() > r) {
577    // reset to begin of file
578  0 this.position = 0;
579  0 this.lineNumber = 0;
580  0 this.column = 0;
581    }
582    for (int i = 0; getRow() < r; i++) {
583    if (EOF == read()) {
584  0 return;
585    }
586    }
587    }
588   
589    /**
590    * Sets the current column position (and indirectly the position).
591    * If <code>column</code> is out of range the minimum value (1) or the maximum possible column
592    * value is taken.
593    *
594    * @param column Move to this column. First column has the number one.
595    */
 
596  61693 toggle public final void setColumn(final int column) {
597  61693 int c = column;
598    // check if column is out of lower bound
599  61693 if (c <= 0) {
600  0 c = 1;
601    }
602    // check if already at wanted position
603  61693 if (getColumn() == c) {
604  0 return;
605    }
606  61693 if (getColumn() > c) {
607  1 do {
608  17 this.position--;
609  17 this.column--;
610  17 } while (getColumn() > c);
611  1 return;
612    }
613  1989628 while (getChar() != CR && getChar() != EOF && getColumn() < c) {
614  1927936 read();
615    }
616    }
617   
618    /**
619    * Show reading position.
620    *
621    * @return current line with mark at current reading position
622    */
 
623  0 toggle public final String showLinePosition() {
624  0 final String line = getLine();
625  0 final StringBuffer buffer = new StringBuffer();
626  0 final int col = getColumn() - 1;
627  0 if (col > 0) {
628  0 if (col < line.length()) {
629  0 buffer.append(line.substring(0, col));
630    } else {
631  0 buffer.append(line);
632    }
633    }
634  0 buffer.append(MARKER);
635  0 if (col < line.length()) {
636  0 buffer.append(line.substring(col));
637    }
638  0 return buffer.toString();
639    }
640   
641    // LATER mime 20050608: remove if no use
642    /*
643    public final int findCaretPosition(final int line, final int column, final String source) {
644    if (line == 1) {
645    return 0;
646    }
647    int k = 1;
648    for (int j = 0; j < source.length(); j++) {
649    if (source.charAt(j) == '\n') {
650    k++;
651    }
652    if (k == line) {
653    j += column - 1;
654    if (j > source.length()) {
655    j = source.length();
656    }
657    return j;
658    }
659    }
660    return 0;
661    }
662    */
663   
664    }