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