Clover Coverage Report
Coverage timestamp: Thu Feb 13 2014 22:50:26 UTC
../../../../img/srcFileCovDistChart8.png 53% of files have more coverage
270   907   169   6
174   487   0.63   45
45     3.76  
1    
 
  TextInput       Line # 32 270 169 77.5% 0.7750511
 
  (62)
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2014, Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10    * This program is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    * GNU General Public License for more details.
14    */
15   
16    package org.qedeq.base.io;
17   
18    import java.io.File;
19    import java.io.IOException;
20    import java.io.InputStream;
21    import java.io.Reader;
22    import java.math.BigInteger;
23   
24    import org.qedeq.base.utility.StringUtility;
25   
26   
27    /**
28    * This class provides convenient methods for parsing input.
29    *
30    * @author Michael Meyling
31    */
 
32    public class TextInput extends InputStream {
33   
34    /** Char marking end of data. */
35    public static final int EOF = -1;
36   
37    /** Char marking end of input line. */
38    // public final static char CR = '\n'; // LATER mime 20050613: delete if running on all platforms
39    public static final char CR = '\012';
40   
41    /** String for marking current reading position. */
42    private static final String MARKER = "#####";
43   
44    /** Holds the data. */
45    private final StringBuffer source;
46   
47    /** Current line number (starting with 0). */
48    private int lineNumber = 0;
49   
50    /** Current column (starting with 0). */
51    private int column = 0;
52   
53    /** Current reading position (starting with 0). */
54    private int position = 0;
55   
56    /** Maximum int value as BigInteger. */
57    private BigInteger maxIntValue = BigInteger.valueOf(Integer.MAX_VALUE);
58   
59    /**
60    * Constructor using <code>Reader</code> source.
61    *
62    * @param reader Data source. This reader will be closed (even if reading fails).
63    * @throws IOException Reading failed.
64    * @throws NullPointerException Argument was a null pointer.
65    */
 
66  2 toggle public TextInput(final Reader reader) throws IOException {
67  2 try {
68  2 if (reader == null) {
69  1 throw new NullPointerException(
70    "no null pointer as argument accepted");
71    }
72  1 this.source = new StringBuffer();
73    // TODO mime 20080307: optimize reading
74  1 int c;
75  ? while (-1 != (c = reader.read())) {
76  832 this.source.append((char) c);
77    }
78    } finally {
79  2 IoUtility.close(reader);
80    }
81    }
82   
83    /**
84    * Constructor using <code>StringBuffer</code> source.
85    *
86    * @param source data source
87    * @throws NullPointerException Argument was a null pointer.
88    */
 
89  4 toggle public TextInput(final StringBuffer source) {
90  4 if (source == null) {
91  2 throw new NullPointerException(
92    "no null pointer as argument accepted");
93    }
94  2 this.source = source;
95    }
96   
97    /**
98    * Constructor using <code>String</code> source.
99    *
100    * @param source data source
101    * @throws NullPointerException Argument was a null pointer.
102    */
 
103  317 toggle public TextInput(final String source) {
104  317 if (source == null) {
105  5 throw new NullPointerException(
106    "no null pointer as argument accepted");
107    }
108  312 this.source = new StringBuffer(source);
109    }
110   
111   
112    /**
113    * Constructor using <code>FILE</code> source.
114    *
115    * @param file Data source.
116    * @param encoding Take this encoding for file.
117    * @throws IOException File reading failed.
118    * @throws NullPointerException One argument was a null pointer.
119    */
 
120  3 toggle public TextInput(final File file, final String encoding) throws IOException {
121  3 if (file == null) {
122  1 throw new NullPointerException(
123    "no null pointer as argument accepted");
124    }
125  2 this.source = new StringBuffer();
126  2 IoUtility.loadFile(file, source, encoding);
127    }
128   
129    /**
130    * Reads a single character and increments the reading position
131    * by one. If no characters are left, <code>-1</code> is returned.
132    * Otherwise a cast to <code>char</code> gives the character read.
133    *
134    * @return Character read, if there are no more chars
135    * <code>-1</code> is returned.
136    */
 
137  16124 toggle public final int read() {
138  16124 if (position >= source.length()) {
139  79 return EOF;
140    }
141  16045 if (getChar() == CR) {
142  449 lineNumber++;
143  449 column = 0;
144    } else {
145  15596 column++;
146    }
147  16045 return source.charAt(position++);
148    }
149   
150    /**
151    * Decrements the reading position by one and reads a single character.
152    * If no characters are left, <code>-1</code> is returned.
153    * Otherwise a cast to <code>char</code> gives the character read.
154    *
155    * @return Character read, if there are no more chars
156    * <code>-1</code> is returned.
157    */
 
158  201 toggle public final int readInverse() {
159  201 if (position <= 0) {
160  2 return -1;
161    }
162  199 final char c = source.charAt(--position);
163  199 if (c == CR) {
164  7 lineNumber--;
165  7 int pos = source.lastIndexOf("" + CR, position - 1);
166  7 if (pos < 0) {
167  5 column = position;
168    } else {
169  2 column = position - 1 - pos;
170    }
171    } else {
172  192 column--;
173  192 if (column < 0) {
174  2 throw new IllegalStateException("column less then 0");
175    }
176    }
177  197 return c;
178    }
179   
180    /**
181    * Reads a given amount of characters and increments the reading position
182    * accordingly.
183    *
184    * @param number amount of characters to read
185    * @return string read
186    */
 
187  362 toggle public final String readString(final int number) {
188  362 final StringBuffer result = new StringBuffer(number);
189  753 for (int i = 0; i < number; i++) {
190  400 final int c = read();
191  400 if (c != -1) {
192  391 result.append((char) c);
193    } else {
194  9 break;
195    }
196    }
197  362 return result.toString();
198    }
199   
200    /**
201    * Skips a given amount of characters and increments the reading position
202    * accordingly.
203    *
204    * @param number Amount of characters to read. Must be positive - if not nothing happens.
205    */
 
206  6 toggle public final void forward(final int number) {
207  838 for (int i = 0; i < number; i++) {
208  833 final int c = read();
209  833 if (c == -1) {
210  1 break;
211    }
212    }
213    }
214   
215    /**
216    * Skips until a given keyword is reached. The position afterwards is at the start
217    * of the keyword or at the end of the text (if the keyword is not found).
218    *
219    * @param search Look for this keyword.
220    * @return Was the keyword found?
221    */
 
222  2 toggle public final boolean forward(final String search) {
223  2 final int pos = source.indexOf(search, position);
224  2 if (pos < 0) {
225  1 setPosition(getMaximumPosition());
226  1 return false;
227    }
228  1 setPosition(pos);
229  1 return true;
230    }
231   
232    /**
233    * Reads a single character and does not change the reading
234    * position. If no characters are left, <code>-1</code> is returned.
235    * Otherwise a cast to <code>char</code> gives the character read.
236    *
237    * @return Character read at current position, if there are no more chars
238    * <code>-1</code> is returned
239    */
 
240  19735 toggle public final int getChar() {
241  19735 if (position >= source.length()) {
242  24 return -1;
243    }
244  19711 return source.charAt(position);
245    }
246   
247    /**
248    * Reads a single character and does not change the reading
249    * position. If offset addition leads out of the source,
250    * <code>-1</code> is returned. Otherwise a cast to <code>char</code>
251    * gives the character read.
252    *
253    * @param skip Offset from current reading position. Maybe negative.
254    * @return Character read, if position is out of scope
255    * <code>-1</code> is returned.
256    */
 
257  346 toggle public final int getChar(final int skip) {
258  346 if (position + skip < 0 || position + skip >= source.length()) {
259  40 return -1;
260    }
261  306 return source.charAt(position + skip);
262    }
263   
264   
265    /**
266    * Reads a substring. Doesn't change reading position. Throws never an Exception.
267    *
268    * @param from Absolute reading position.
269    * @param to Read to this position.
270    * @return Resulting string.
271    */
 
272  25 toggle public final String getSubstring(final int from, final int to) {
273  25 if (from >= to) {
274  8 return "";
275    }
276  17 final int l = source.length();
277  13 final int f = (from < 0 ? 0 : (from > l ? l : from));
278  17 final int t = (to < 0 ? 0 : (to > l ? l : to));
279  17 return source.substring(f, t);
280    }
281   
282    /**
283    * Get complete input source. Doesn't change reading position.
284    *
285    * @return Complete input string.
286    */
 
287  2 toggle public final String asString() {
288  2 return source.toString();
289    }
290   
291    /**
292    * Replace given interval with given string.
293    * If the current reading position is in the interval it is set
294    * to the end of the interval.
295    *
296    * @param from Absolute reading position.
297    * @param to Read to this position.
298    * @param replacement Replacement for interval.
299    */
 
300  3 toggle public final void replace(final int from, final int to, final String replacement) {
301  3 source.replace(from, to, replacement);
302  3 if (position > from && position < to) {
303  0 setPosition(from + replacement.length());
304  3 } else if (position > from) { // correct row (and column) information
305  2 setPosition(position - to + from + replacement.length());
306    }
307    }
308   
309    /**
310    * Skips white space, beginning from reading position.
311    * Changes reading position to next non white space
312    * character.
313    */
 
314  782 toggle public final void skipWhiteSpace() {
315  902 while (!isEmpty() && Character.isWhitespace((char) getChar())) {
316  120 read();
317    }
318    }
319   
320    /**
321    * Skips white space, beginning from reading position.
322    * Changes reading position to next non white space
323    * character.
324    */
 
325  6 toggle public final void skipWhiteSpaceInverse() {
326  26 while (getPosition() > 0 && Character.isWhitespace((char) getChar(-1))) {
327  20 readInverse();
328    }
329    }
330   
331    /**
332    * Skip current position back to beginning of an XML tag.
333    * This is mainly something like <code>&lt;tagName</code>.
334    *
335    * @throws IllegalArgumentException No begin of XML tag found.
336    */
 
337  8 toggle public final void skipBackToBeginOfXmlTag() {
338  8 if ('<' == getChar()) {
339  2 return;
340    }
341  6 boolean quoted = false;
342  6 do {
343  172 if (-1 == readInverse()) {
344  0 throw new IllegalArgumentException("begin of xml tag not found");
345    }
346  172 if ('\"' == getChar()) {
347  16 quoted = !quoted;
348    }
349  172 } while (quoted || '<' != getChar());
350    }
351   
352    /**
353    * Skip forward to end of line.
354    */
 
355  7 toggle public final void skipToEndOfLine() {
356  7 int c = 0;
357  7 do {
358  60 c = read();
359  60 } while (!isEmpty() && c != CR);
360    }
361   
362    /**
363    * Skip current position forward to end of an XML tag. It is assumed the current position is
364    * within the the XML tag. Now we search for <code>&gt;</code>. Quoted data is skipped.
365    *
366    * @throws IllegalArgumentException No end of XML tag found.
367    */
 
368  6 toggle public final void skipForwardToEndOfXmlTag() {
369  6 if ('>' == getChar()) {
370  0 return;
371    }
372  6 boolean quoted = false;
373  317 while (!isEmpty() && (quoted || '>' != getChar())) {
374  311 int c = read();
375  311 if ('\"' == c) {
376  21 quoted = !quoted;
377    }
378    }
379  6 if (isEmpty()) {
380  2 throw new IllegalArgumentException("end of xml tag not found");
381    }
382  4 read(); // skip '>'
383    }
384   
385    /**
386    * Reads tag or attribute name out of XML stream. Whitespace is skipped and
387    * characters are read till &quot;=&quot; or &quot;&gt;&quot; or &quot;&lt; or whitespace is
388    * found. We must be within the tag, so we can not start with something like &lt;.
389    *
390    * @return Name of tag or attribute.
391    * @throws IllegalArgumentException Next non white space character is &quot;=&quot;
392    * or &quot;&gt;&quot;.
393    */
 
394  13 toggle public final String readNextXmlName() {
395  13 skipWhiteSpace();
396  13 if (isEmpty() || '=' == getChar() || '>' == getChar() || '<' == getChar()) {
397  3 throw new IllegalArgumentException(
398    "begin of attribute or tag expected");
399    }
400  10 StringBuffer buffer = new StringBuffer();
401  42 while (!isEmpty() && '=' != getChar() && '>' != getChar() && '<' != getChar()
402    && !Character.isWhitespace((char) getChar())) {
403  32 buffer.append((char) read());
404    }
405  10 return buffer.toString();
406    }
407   
408    /**
409    * Reads attribute value out of XML stream. Whitespace is skipped and an &quot;=&quot;
410    * is expected to follow. Again whitespace is skipped. If no quotation mark follows
411    * characters are read till whitespace or &quot;&gt;&quot; occurs. Otherwise data is
412    * read till an ending quotation mark comes.
413    *
414    * @return Value read.
415    * @throws IllegalArgumentException Following had not one of the following forms:
416    * <pre>
417    * = "value"
418    * </pre>
419    * <pre>
420    * = value
421    * </pre>
422    */
 
423  13 toggle public final String readNextAttributeValue() {
424  13 skipWhiteSpace();
425  13 if (isEmpty() || '=' != getChar()) {
426  6 throw new IllegalArgumentException(
427    "\"=\" expected");
428    }
429  7 read(); // read =
430  7 skipWhiteSpace();
431  7 if (isEmpty() || '>' == getChar()) {
432  0 throw new IllegalArgumentException(
433    "attribute value expected");
434    }
435  7 StringBuffer buffer = new StringBuffer();
436  7 if ('\"' == getChar()) {
437  5 read(); // read "
438  56 while (!isEmpty() && '\"' != getChar()) {
439  51 buffer.append((char) read());
440    }
441  5 if ('\"' != getChar()) {
442  2 throw new IllegalArgumentException("\" expected");
443    }
444  3 read(); // read "
445    } else {
446  10 while (!isEmpty() && '>' != getChar()
447    && !Character.isWhitespace((char) getChar())) {
448  8 buffer.append((char) read());
449    }
450    }
451  5 return StringUtility.unescapeXml(buffer.toString());
452    }
453   
454    /**
455    * Is there no data left for reading?
456    *
457    * @return is all data read?
458    */
 
459  9684 toggle public final boolean isEmpty() {
460  9684 return position >= source.length();
461    }
462   
463    /**
464    * Is there no data left for reading after skipping?
465    *
466    * @param skip Add this number to current position.
467    * @return Is data empty at that new position?
468    */
 
469  16 toggle public final boolean isEmpty(final int skip) {
470  16 return position + skip >= source.length();
471    }
472   
473    /**
474    * Reads the next string containing only letters or digits,
475    * leading whitespace is skipped.
476    * Changes reading position.
477    *
478    * @return read string
479    * @throws IllegalArgumentException if no such characters could
480    * be found
481    */
 
482  13 toggle public final String readLetterDigitString() {
483  13 skipWhiteSpace();
484  13 if (isEmpty() || !Character.isLetterOrDigit((char) getChar())) {
485  4 read(); // for showing correct position
486  4 throw new IllegalArgumentException(
487    "letter or digit expected");
488    }
489  9 StringBuffer buffer = new StringBuffer();
490  68 while (!isEmpty() && Character.isLetterOrDigit((char) getChar())) {
491  59 buffer.append((char) read());
492    }
493  9 return buffer.toString();
494    }
495   
496    /**
497    * Reads the next string until whitespace occurs,
498    * leading whitespace is skipped.
499    * Changes (probably) reading position.
500    *
501    * @return read string
502    */
 
503  0 toggle public final String readStringTilWhitespace() {
504  0 skipWhiteSpace();
505  0 StringBuffer buffer = new StringBuffer();
506  0 while (!isEmpty() && !Character.isWhitespace((char) getChar())) {
507  0 buffer.append((char) read());
508    }
509  0 return buffer.toString();
510    }
511   
512    /**
513    * Reads the next integer, leading whitespace is skipped. Signs like - or + are not
514    * accepted. Resulting integer
515    * Changes reading position.
516    *
517    * @return Read integer.
518    * @throws IllegalArgumentException if no digits where found or
519    * the number was to big for an <code>int</code>
520    */
 
521  533 toggle public final int readNonNegativeInt() {
522  533 skipWhiteSpace();
523  533 if (isEmpty() || !Character.isDigit((char) getChar())) {
524  12 read(); // for showing correct position
525  12 throw new IllegalArgumentException(
526    "digit expected");
527    }
528  521 StringBuffer buffer = new StringBuffer();
529  1460 while (!isEmpty() && Character.isDigit((char) getChar())) {
530  939 buffer.append((char) read());
531    }
532  521 final BigInteger big = new BigInteger(buffer.toString());
533  521 if (1 == big.compareTo(maxIntValue)) {
534  2 throw new IllegalArgumentException("this integer is to large! Maximum possible value is "
535    + maxIntValue);
536    }
537  519 return big.intValue();
538    }
539   
540    /**
541    * Reads the next (big) integer, leading whitespace is skipped.
542    * The first character might be a minus sign, the rest must be
543    * digits. Leading zero digits are not allowed, also "-0" is not
544    * accepted. <p>
545    * Changes reading position.
546    *
547    * @return read integer
548    * @throws IllegalArgumentException if no digits where found.
549    */
 
550  12 toggle public final String readCounter() {
551  12 skipWhiteSpace();
552  12 if (isEmpty()) {
553  2 throw new IllegalArgumentException("integer expected");
554    }
555  10 StringBuffer buffer = new StringBuffer();
556  10 if (getChar() == '-') {
557  0 buffer.append(read());
558    }
559  10 final int begin = getPosition();
560  10 if (!Character.isDigit((char) getChar())) {
561  4 throw new IllegalArgumentException("digit expected");
562    }
563  32 while (!isEmpty() && Character.isDigit((char) getChar())) {
564  26 buffer.append((char) read());
565    }
566  6 if (buffer.length() >= 2 && ('0' == buffer.charAt(0)
567    || '-' == buffer.charAt(0) && '0' == buffer.charAt(1))) {
568  2 setPosition(begin); // for showing correct position
569  2 throw new IllegalArgumentException("no leading zeros allowed");
570    }
571  4 return buffer.toString();
572    }
573   
574    /**
575    * Reads the next quoted string, leading whitespace is skipped.
576    * A correctly quoted string could be created by adding a leading and
577    * a trailing quote character and doubling each other quote character.
578    * The resulting string is dequoted.
579    * Changes reading position.
580    *
581    * @return Dequoted string read.
582    * @throws IllegalArgumentException No correctly quoted string was found.
583    */
 
584  10 toggle public final String readQuoted() {
585  10 skipWhiteSpace();
586  10 if (isEmpty() || read() != '\"') {
587  2 throw new IllegalArgumentException(
588    "\" expected");
589    }
590  8 StringBuffer unquoted = new StringBuffer();
591  8 char c;
592  8 do {
593  28 if (isEmpty()) {
594  0 throw new IllegalArgumentException(
595    "ending \" expected");
596    }
597  28 c = (char) read();
598  28 if (c != '\"') {
599  18 unquoted.append(c);
600    } else { // c == '\"'
601  10 if (isEmpty() || getChar() != '\"') {
602  8 break; // success
603    }
604  2 unquoted.append((char) read());
605    }
606    } while (true);
607  8 return unquoted.toString();
608    }
609   
610    /**
611    * Returns the current line number.
612    *
613    * @return Current line number (starting with line 1).
614    */
 
615  2922 toggle public final int getRow() {
616  2922 return lineNumber + 1;
617    }
618   
619    /**
620    * Returns the current column number.
621    *
622    * @return Current column number (starting with line 1).
623    */
 
624  64 toggle public final int getColumn() {
625  64 return column + 1;
626    }
627   
628    /**
629    * Returns the current line.
630    *
631    * @return Current line.
632    */
 
633  4 toggle public final String getLine() {
634  4 int min = position - 1;
635  4 while (min >= 0 && source.charAt(min) != CR) {
636  0 min--;
637    }
638  4 int max = position;
639  244 while (max < source.length()
640    && source.charAt(max) != CR) {
641  240 max++;
642    }
643  4 if (min + 1 >= max) {
644  0 return "";
645    }
646  4 return source.substring(min + 1, max);
647    }
648   
649    /**
650    * Returns the current position. Starting with 0. This is the number of characters
651    * from the beginning.
652    *
653    * @return Current position.
654    */
 
655  82 toggle public final int getPosition() {
656  82 return position;
657    }
658   
659    /**
660    * Returns the current position.
661    *
662    * @return Current position.
663    */
 
664  0 toggle public final SourcePosition getSourcePosition() {
665  0 return new SourcePosition(getRow(), getColumn());
666    }
667   
668    /**
669    * Returns the highest position number possible. This is equal
670    * to the length of the source.
671    *
672    * @return Maximum position.
673    */
 
674  11 toggle public final int getMaximumPosition() {
675  11 return source.length();
676    }
677   
678    /**
679    * Sets the current position (and indirectly the row and column number).
680    *
681    * @param position Set current position to this value.
682    */
 
683  42 toggle public final void setPosition(final int position) {
684  42 if (position >= source.length()) {
685  14 this.position = source.length();
686  28 } else if (this.position != position) {
687  27 if (position < this.position) {
688  17 this.position = 0;
689  17 this.lineNumber = 0;
690  17 this.column = 0;
691  1689 for (int i = 0; i < position; i++) { // Q & D
692  1672 read();
693    }
694    } else {
695  368 for (int i = this.position; i < position; i++) {
696  358 read();
697    }
698    }
699    }
700    }
701   
702    /**
703    * Sets the current position (and indirectly the row and column number).
704    *
705    * @param position Set current position to this value.
706    */
 
707  0 toggle public final void setPosition(final SourcePosition position) {
708  0 setRow(position.getRow());
709  0 setColumn(position.getColumn());
710    }
711   
712    /**
713    * Adds a given position to the current one and changes the row and column number accordingly.
714    * A delta position with one row and one column doesn't change the current position.
715    *
716    * @param delta Add this position to current one.
717    */
 
718  0 toggle public final void addPosition(final SourcePosition delta) {
719  0 addRow(delta.getRow() - 1);
720  0 addColumn(delta.getColumn() - 1);
721    }
722   
723    /**
724    * Sets the current line number (and indirectly the position).
725    *
726    * @param row Move to this line number.
727    */
 
728  12 toggle public final void setRow(final int row) {
729  12 int r = row;
730    // check if row is under lower bound
731  12 if (r <= 0) {
732  0 r = 1;
733    }
734    // check if already at wanted position
735  12 if (getRow() == r) {
736  4 return;
737    }
738    // check if already at end of file
739  8 if (getPosition() >= source.length() && getRow() >= r) {
740  0 return;
741    }
742  8 if (getRow() > r) {
743    // reset to begin of file
744  0 this.position = 0;
745  0 this.lineNumber = 0;
746  0 this.column = 0;
747    }
748  2898 while (getRow() < r) {
749  2890 if (EOF == read()) {
750  0 return;
751    }
752    }
753    }
754   
755    /**
756    * Get given byte position as {@link SourcePosition}.
757    *
758    * @param find Get row and column information for this byte position.
759    * @return Row and column information.
760    */
 
761  0 toggle public final SourcePosition getPosition(final int find) {
762  0 int r = 0;
763  0 int c = 0;
764  0 int i = 0;
765  0 while (i < source.length() && i < find) {
766  0 if (CR == source.charAt(i)) {
767  0 r++;
768  0 c = 0;
769    } else {
770  0 c++;
771    }
772  0 i++;
773    }
774  0 return new SourcePosition(r + 1, c + 1);
775    }
776   
777    /**
778    * Get given byte position as {@link SourcePosition}.
779    *
780    * @param position Get row and column information for this byte position.
781    * @return Row and column information.
782    */
 
783  0 toggle public final int getPosition(final SourcePosition position) {
784  0 int find = 0;
785  0 int r = 0;
786   
787  0 while (++r < position.getRow()) {
788  0 find = source.indexOf("" + CR, find);
789  0 if (-1 == find) {
790  0 break;
791    }
792    }
793  0 if (find < 0) {
794  0 find = source.length();
795    }
796  0 find += position.getColumn();
797  0 if (find > source.length()) {
798  0 find = source.length();
799    }
800  0 return find;
801    }
802   
803    /**
804    * Get source area as string.
805    *
806    * @param area Get this area as string.
807    * @return Area itself.
808    */
 
809  0 toggle public final String getSourceArea(final SourceArea area) {
810  0 return source.substring(getPosition(area.getStartPosition()),
811    getPosition(area.getEndPosition()));
812    }
813   
814    /**
815    * Add the following rows and reset column (if <code>number == 0</code>).
816    *
817    * @param number Add this number of rows.
818    */
 
819  0 toggle public final void addRow(final int number) {
820  0 setRow(getRow() + number);
821    }
822   
823    /**
824    * Sets the current column position (and indirectly the position).
825    * If <code>column</code> is out of range the minimum value (1) or the maximum possible column
826    * value is taken.
827    *
828    * @param column Move to this column. First column has the number one.
829    */
 
830  4 toggle public final void setColumn(final int column) {
831  4 int c = column;
832    // check if column is out of lower bound
833  4 if (c <= 0) {
834  0 c = 1;
835    }
836    // check if already at wanted position
837  4 if (getColumn() == c) {
838  0 return;
839    }
840  4 if (getColumn() > c) {
841  2 do {
842  34 this.position--;
843  34 this.column--;
844  34 } while (getColumn() > c);
845  2 return;
846    }
847  18 while (getChar() != CR && getChar() != EOF && getColumn() < c) {
848  16 read();
849    }
850    }
851   
852    /**
853    * Add the following columns.
854    *
855    * @param number Add this number of columns.
856    */
 
857  0 toggle public final void addColumn(final int number) {
858  0 setColumn(getColumn() + number);
859    }
860   
861    /**
862    * Show reading position.
863    *
864    * @return current line with mark at current reading position
865    */
 
866  0 toggle public final String showLinePosition() {
867  0 final String line = getLine();
868  0 final StringBuffer buffer = new StringBuffer();
869  0 final int col = getColumn() - 1;
870  0 if (col > 0) {
871  0 if (col < line.length()) {
872  0 buffer.append(line.substring(0, col));
873    } else {
874  0 buffer.append(line);
875    }
876    }
877  0 buffer.append(MARKER);
878  0 if (col < line.length()) {
879  0 buffer.append(line.substring(col));
880    }
881  0 return buffer.toString();
882    }
883   
884    // LATER mime 20050608: remove if no use
885    /*
886    public final int findCaretPosition(final int line, final int column, final String source) {
887    if (line == 1) {
888    return 0;
889    }
890    int k = 1;
891    for (int j = 0; j < source.length(); j++) {
892    if (source.charAt(j) == '\n') {
893    k++;
894    }
895    if (k == line) {
896    j += column - 1;
897    if (j > source.length()) {
898    j = source.length();
899    }
900    return j;
901    }
902    }
903    return 0;
904    }
905    */
906   
907    }