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