Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 473   Methods: 24
NCLOC: 239   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
TokenList.java 81.5% 89.5% 95.8% 87.5%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.model.definitions.reducedmodel;
 38   
 39    /** A list of reduced model tokens. Uses ModelList as its base.
 40    * @version $Id: TokenList.java 5175 2010-01-20 08:46:32Z mgricken $
 41    */
 42    public class TokenList extends ModelList<ReducedToken> implements /*imports*/ ReducedModelStates {
 43   
 44    /** Gets a TokenList.Iterator for this list. Overrides the weaker method in ModelList<ReducedToken>.Iterator. */
 45  3164 public Iterator getIterator() { return new Iterator(); }
 46   
 47    public class Iterator extends ModelIterator {
 48   
 49    private int _offset;
 50   
 51  3164 public Iterator() {
 52  3164 super();
 53  3164 _offset = 0;
 54    }
 55   
 56  59587 private Iterator(Iterator that) {
 57  59587 super(that);
 58  59587 _offset = that.getBlockOffset();
 59    }
 60   
 61    /** Makes a fresh copy of this TokenList.Iterator. copy() returns a ModelList<ReducedToken>.Iterator copy which is
 62    * more restrictive than TokenList.Iterator. An underscore differentiates the two methods. This differentiation
 63    * was easiest since it allowed us to keep TokenList.Iterator extending ModelList<ReducedToken>.Iterator.
 64    */
 65  59587 public Iterator copy() { return new Iterator(this); }
 66   
 67  490 public void setTo(Iterator that) {
 68  490 super.setTo(that);
 69  490 _offset = that.getBlockOffset();
 70    }
 71   
 72  123459 public int getBlockOffset() { return _offset; }
 73   
 74  23790 public void setBlockOffset(int offset) { _offset = offset; }
 75   
 76    /** Returns the current commented/quoted state at the cursor.
 77    * @return FREE | INSIDE_BLOCK_COMMENT | INSIDE_LINE_COMMENT | INSIDE_SINGLE_QUOTE | INSIDE_DOUBLE_QUOTE
 78    */
 79  300548 public ReducedModelState getStateAtCurrent() {
 80  3599 if (atFirstItem() || atStart() || TokenList.this.isEmpty()) return FREE;
 81  296949 else if (prevItem().isLineComment() || (prevItem().getState() == INSIDE_LINE_COMMENT))
 82  583 return INSIDE_LINE_COMMENT;
 83  296366 else if (prevItem().isBlockCommentStart() || (prevItem().getState() == INSIDE_BLOCK_COMMENT))
 84  14292 return INSIDE_BLOCK_COMMENT;
 85  282074 else if ((prevItem().isDoubleQuote() && prevItem().isOpen() && (prevItem().getState() == FREE)) ||
 86    (prevItem().getState() == INSIDE_DOUBLE_QUOTE))
 87  749 return INSIDE_DOUBLE_QUOTE;
 88  281325 else if ((prevItem().isSingleQuote() && prevItem().isOpen() && (prevItem().getState() == FREE)) ||
 89    (prevItem().getState() == INSIDE_SINGLE_QUOTE))
 90  216 return INSIDE_SINGLE_QUOTE;
 91  281109 else return FREE;
 92    }
 93   
 94   
 95    /** Handles the details of the case where a brace is inserted into a gap. Assumes the current token is a gap!
 96    * Assumes that read lock and reduced locks are already held.
 97    */
 98  72 void insertBraceToGap(String text) {
 99  72 current().shrink(getBlockOffset());
 100  72 insert(Brace.MakeBrace(text, getStateAtCurrent()));
 101    // add a new gap to account for the remainder from the split gap
 102    // if block offset is zero, do NOT add a Gap of size 0.
 103  72 if (getBlockOffset() > 0) {
 104  68 insert(new Gap(getBlockOffset(), getStateAtCurrent()));
 105  68 next(); //now point at new brace
 106    }
 107  72 next(); // now pointing at second half of gap
 108  72 setBlockOffset(0);
 109    }
 110   
 111    /** Helper function to _insertBrace. Handles the details of the case where brace is inserted between two
 112    * reduced tokens. No destructive action is taken. Assume that read lock and reduced lock are already held.
 113    */
 114  7185 void insertNewBrace(String text) {
 115  7185 insert(Brace.MakeBrace(text, getStateAtCurrent()));
 116  7185 next();
 117  7185 setBlockOffset(0);
 118    }
 119   
 120    /** Splits the current brace if it is a multiple character brace and fulfills certain conditions. If the current
 121    * brace is a // or /*, split it into two braces. Do the same for star-slash (end comment block) if the parameter
 122    * splitClose is true. Do the same for \\ and \" if splitEscape is true. If a split was performed, the first of
 123    * the two Braces will be the current one when we're done. The offset is not changed. The two new Braces will
 124    * have the same quoted/commented status as the one they were split from.
 125    */
 126  1667 void _splitCurrentIfCommentBlock(boolean splitClose, boolean splitEscape) {
 127  1667 String type = current().getType();
 128  1667 if (type.equals("//") || type.equals("/*") ||
 129    (splitClose && type.equals("*/")) ||
 130    (splitEscape && type.equals("\\\\")) ||
 131    (splitEscape && type.equals("\\\"")) ||
 132    (splitEscape && type.equals("\\'"))) {
 133  43 String first = type.substring(0, 1);
 134  43 String second = type.substring(1, 2);
 135    // change current Brace to only be first character
 136  43 current().setType(first);
 137  43 ReducedModelState oldState = current().getState();
 138   
 139    // then put a new brace after the current one
 140  43 next();
 141  43 insert(Brace.MakeBrace(second, oldState));
 142    // Move back to make the first brace we inserted current
 143  43 prev();
 144    }
 145    }
 146   
 147    /** Walks along the list on which ReducedModel is based from the current cursor position. Which path it takes
 148    * depends on the return value of getStateAtCurrent() at the start of the walk. Assumes read lock and reduced
 149    * lock are already held.
 150    */
 151  5259 void updateBasedOnCurrentState() {
 152  0 if (atStart()) next();
 153   
 154    // If there's no text after here, nothing to update!
 155  764 if (atEnd()) return;
 156   
 157  4495 ReducedModelState curState = getStateAtCurrent();
 158    // Free if at the beginning
 159  6165 while (! atEnd()) { curState = curState.update(this); }
 160    }
 161   
 162    /** Updates the BraceReduction to reflect cursor movement. Negative values move left from the cursor, positive
 163    * values move right. ASSUMES that count is within range, i.e. that the move will not push cursor past start
 164    * or end.
 165    * @param count indicates the direction and magnitude of cursor movement
 166    */
 167  974059 public void move(int count) { _offset = _move(count, _offset); }
 168   
 169    /** Helper function for move(int). Assumes that count is in range!
 170    * @param count the number of chars to move. Negative values move back, positive values move forward.
 171    * @param currentOffset the current offset for copyCursor
 172    * @return the updated offset
 173    */
 174  974059 private int _move(int count, int currentOffset) {
 175  77059 if (count == 0) return currentOffset;
 176  897000 Iterator it = this;
 177   
 178    //make copy of cursor and return new iterator?
 179  298765 if (count > 0) return it._moveRight(count, currentOffset);
 180  598235 return it._moveLeft(- count, currentOffset); // count < 0
 181    }
 182   
 183    /** Helper function that moves cursor ([iterator pos, count]) forward by count chars. Assumes that count > 0 and
 184    * is in range. Returns the new count.
 185    * <ol>
 186    * <li> at head && count > 0: next
 187    * <li> LOOP:<BR>
 188    * if atEnd and count == 0, stop<BR>
 189    * if atEnd and count > 0, throw boundary exception<BR>
 190    * if count < size of current token, offset = count, stop<BR>
 191    * otherwise, reduce count by size of current token and go to
 192    * the next token, continuing the loop.
 193    * </ol>
 194    */
 195  298765 private int _moveRight(int count, int currentOffset) {
 196    // Standardize initial position
 197  298765 if (atStart()) {
 198  1 currentOffset = 0;
 199  1 next();
 200    }
 201  1 if (atEnd()) throw new IllegalArgumentException("At end");
 202   
 203    // Initialize loop variables
 204  298764 int size = current().getSize();
 205  298764 count = count + currentOffset;
 206   
 207    // Process tokens moving forward
 208  298764 while (count >= size) { // advance one token
 209  11911079 count = count - size;
 210  11911079 next();
 211  11911079 if (atEnd()) {
 212  3886 if (count == 0) break;
 213  0 else throw new IllegalArgumentException("At end");
 214    }
 215  11907193 size = current().getSize();
 216    }
 217  298764 return count; // returns the offset in the current token
 218    }
 219   
 220    /** Helper function that moves cursor ([iterator pos, count]) backward by count chars. Assumes that count > 0 and
 221    * is in range. Returns the new count.
 222    * <ol>
 223    * <li> atEnd && count > 0: prev
 224    * <li> LOOP:<BR>
 225    * if atStart and count == 0, stop<BR>
 226    * if atStart and count > 0, throw boundary exception<BR>
 227    * if count < size of current token, offset = size - count, stop<BR>
 228    * otherwise, reduce count by size of current token and go to
 229    * the previous token, continuing the loop.
 230    * </ol>
 231    */
 232  598235 private int _moveLeft(int count, int currentOffset) {
 233   
 234    // Standardize initial position, eliminating 0 offset
 235  598235 if (atEnd()) {
 236  4477 assert currentOffset == 0;
 237  4477 prev();
 238  0 if (atStart()) throw new IllegalArgumentException("At Start");
 239  4477 currentOffset = current().getSize(); // ! atStart() is precondition for calling current()
 240    }
 241  1 else if (atStart()) throw new IllegalArgumentException("At Start");
 242   
 243  598234 while (count > currentOffset) {
 244  11911505 count = count - currentOffset;
 245  11911505 prev();
 246   
 247  0 if (atStart()) throw new IllegalArgumentException("At Start"); // count > 0
 248  11911505 currentOffset = current().getSize();
 249    }
 250  598234 return currentOffset - count; // Note: returned offset can be 0
 251    }
 252   
 253    /** <P>Update the BraceReduction to reflect text deletion.</P>
 254    * @param count A number specifying the size and direction of text deletion. Negative values delete text to the
 255    * left of the cursor; positive values delete text to the right. Assumes deletion is within range!
 256    */
 257  674 public void delete(int count) {
 258  0 if (count == 0) return;
 259  674 Iterator copyCursor = copy();
 260    // from = this iterator
 261    // to = this iterator's copy
 262  674 _offset = _delete(count, copyCursor);
 263  674 copyCursor.dispose();
 264  674 return;
 265    }
 266   
 267    /** Helper function for delete. If deleting forward, move delTo the distance forward and call
 268    * deleteRight.<BR> If deleting backward, move delFrom the distance back and call deleteRight.
 269    * @param count size of deletion
 270    * @param copyCursor cursor iterator
 271    * @return new offset after deletion
 272    */
 273  674 private int _delete(int count, Iterator copyCursor) {
 274    // Guarrantees that it's possible to delete count characters
 275  674 try {
 276  642 if (count > 0) copyCursor.move(count);
 277  32 else move(count); // count <= 0
 278  674 return deleteRight(copyCursor);
 279    }
 280  0 catch (Exception e) { throw new IllegalArgumentException("Trying to delete past end of file."); }
 281    }
 282   
 283    /** Gets rid of extra text. Because collapse cannot get rid of all deletion text as some may be
 284    * only partially spanning a token, we need to make sure that
 285    * this partial span into the non-collapsed token on the left is removed.
 286    */
 287  483 void clipLeft() {
 288  0 if (atStart()) return;
 289  406 else if (getBlockOffset() == 0) remove();
 290  77 else if (current().isGap()) {
 291  68 int size = current().getSize();
 292  68 current().shrink(size - getBlockOffset());
 293    }
 294  9 else if (current().isMultipleCharBrace()) {
 295  0 if (getBlockOffset() != 1) throw new IllegalArgumentException("Offset incorrect");
 296    else {
 297  9 String type = current().getType();
 298  9 String first = type.substring(0, 1);
 299  9 current().setType(first);
 300    }
 301    }
 302  0 else throw new IllegalArgumentException("Cannot clip left.");
 303    }
 304   
 305    /** Gets rid of extra text. Because collapse cannot get rid of all deletion text as some may only partially span a
 306    * token, we must remove this partial span into the non-collapsed token on the right.
 307    */
 308  490 void clipRight() {
 309  420 if (atEnd()) return;
 310  59 else if (getBlockOffset() == 0) return;
 311  0 else if (getBlockOffset() == current().getSize()) remove();
 312  1 else if (current().isGap()) current().shrink(getBlockOffset());
 313  10 else if (current().isMultipleCharBrace()) {
 314  0 if (getBlockOffset() != 1) throw new IllegalArgumentException("Offset incorrect");
 315    else {
 316  10 String type = current().getType();
 317  10 String second = type.substring(1, 2);
 318  10 current().setType(second);
 319    }
 320    }
 321  0 else throw new IllegalArgumentException("Cannot clip left.");
 322    }
 323   
 324    /** Deletes from offset in this to endOffset in delTo. Uses ModelList.collapse to perform quick deletion. */
 325  674 int deleteRight(Iterator delTo) {
 326  674 collapse(delTo);
 327   
 328    // if both pointing to same item, and it's a gap
 329  674 if (eq(delTo) && current().isGap()) {
 330    // inside gap
 331  184 current().shrink(delTo.getBlockOffset() - getBlockOffset());
 332  184 return getBlockOffset();
 333    }
 334   
 335   
 336    //if brace is multiple char it must be a comment because the above if
 337    //test guarrantees it can't be a gap.
 338  483 if (! eq(delTo)) clipLeft();
 339  490 delTo.clipRight();
 340   
 341  490 if (! atStart()) prev();
 342  490 int delToSizeCurr;
 343  490 String delToTypeCurr;
 344  490 if (delTo.atEnd()) {
 345  420 setTo(delTo);
 346  420 return 0;
 347    }
 348    else {
 349  70 delToSizeCurr = delTo.current().getSize();
 350  70 delToTypeCurr = delTo.current().getType();
 351    }
 352   
 353    //get info on previous item.
 354  70 delTo.prev(); //get stats on previous item
 355   
 356  70 int delToSizePrev;
 357  70 String delToTypePrev;
 358  70 if (delTo.atStart()) { //no previous item, can't be at end
 359  12 delTo.next();
 360  12 setTo(delTo);
 361  12 return 0;
 362    }
 363    else {
 364  58 delToSizePrev = delTo.current().getSize();
 365  58 delToTypePrev = delTo.current().getType();
 366    }
 367  58 delTo.next(); //put delTo back on original node
 368   
 369  58 int temp = _calculateOffset(delToSizePrev, delToTypePrev,
 370    delToSizeCurr, delToTypeCurr,
 371    delTo);
 372  58 this.setTo(delTo);
 373  58 return temp;
 374    }
 375   
 376   
 377    /** By comparing the delTo token after the walk to what it was before the walk we can see how it has changed and
 378    * where the offset should go.<p/>
 379    * Prev is the item previous to the current cursor. Curr is the current token. delTo is where current is pointing
 380    * at this moment in time.
 381    */
 382  58 private int _calculateOffset(int delToSizePrev, String delToTypePrev, int delToSizeCurr, String delToTypeCurr,
 383    Iterator delTo) {
 384  58 int offset;
 385  58 int delToSizeChange = delTo.current().getSize();
 386    // String delToTypeChange = delTo.current().getType();
 387   
 388    //1)if there was a gap previous to the gap at delTo delTo should be
 389    //augmented by its size, and that size is the offset.
 390    //2)if the gap was not preceeded by a gap then it would not need to
 391    //be shrunk
 392  0 if (delTo.atEnd()) throw new IllegalArgumentException("Shouldn't happen");
 393  25 if (delTo.current().isGap()) return delToSizeChange - delToSizeCurr;
 394   
 395    //this means that the item at the end formed a double brace with the
 396    //item that the delete left preceeding it. /dddddd*
 397   
 398    //the final item shrunk. This can only happen if the starting item
 399    //stole one of its braces: /ddddd*/
 400    //or if it was a double brace that had to get broken because it was
 401    //now commented or no longer has an open block
 402   
 403    //EXAMPLES: /*___*/ becoming */
 404    // /*___*/ delete the first star, through the spaces to get
 405    // /*/
 406    // //*__\n// becoming //*__//, the // is broken
 407    // //*__\n// becoming //// , the // is broken
 408    //THIS MUST HAVE THE previous items size and type passed in from
 409    //before the update. This way we know how it's changing too.
 410   
 411    // In this if clause, special characters are initially separated by some text
 412    // (represented here as ellipses), and when the text is deleted, the special
 413    // characters come together. Sometimes, this breaks up the second token if
 414    // it is a multiple character brace. Each in-line comment demonstrates
 415    // the individual case that occurs and for which we check with this if.
 416    // In this branch, both the cursor is off and the offset is also not correct.
 417  33 if ((delToTypePrev.equals("/") &&
 418    // /.../* => //-*
 419    ((delToTypeCurr.equals("/*") && _checkPrevEquals(delTo, "//")) ||
 420    // /...// => //-/
 421    (delToTypeCurr.equals("//") && _checkPrevEquals(delTo, "//")))) ||
 422   
 423    (delToTypePrev.equals("*") &&
 424    // *.../* => */-*
 425    ((delToTypeCurr.equals("/*") && _checkPrevEquals(delTo, "*/")) ||
 426    // *...// => */-/
 427    (delToTypeCurr.equals("//") && _checkPrevEquals(delTo, "*/")))) ||
 428   
 429    (delToTypePrev.equals("\\") &&
 430    // \...\\ => \\-\
 431    ((delToTypeCurr.equals("\\\\") && _checkPrevEquals(delTo, "\\")) ||
 432    // \...\' => \\-'
 433    (delToTypeCurr.equals("\\'") && _checkPrevEquals(delTo, "'")) ||
 434    // \...\" => \\-"
 435    (delToTypeCurr.equals("\\\"") && _checkPrevEquals(delTo, "\""))))) {
 436  0 delTo.prev();
 437  0 offset = 1;
 438    }
 439    // In this branch, the cursor is on the right token, but the offset is not correct.
 440  33 else if ((delToTypePrev.equals("/") &&
 441    // /-*/
 442    ((delToTypeCurr.equals("*/") && delTo.current().getType().equals("/*")) ||
 443    (delToTypeCurr.equals("*") && delTo.current().getType().equals("/*")) ||
 444    (delToTypeCurr.equals("/") && delTo.current().getType().equals("//")))) ||
 445   
 446    (delToTypePrev.equals("*") &&
 447    delToTypeCurr.equals("/") && delTo.current().getType().equals("*/")) ||
 448   
 449    (delToTypePrev.equals("\\") &&
 450    ((delToTypeCurr.equals("\\") && delTo.current().getType().equals("\\\\")) ||
 451    (delToTypeCurr.equals("'") && delTo.current().getType().equals("\\'")) ||
 452    (delToTypeCurr.equals("\"") && delTo.current().getType().equals("\\\""))))) {
 453  0 offset = 1;
 454    }
 455    // Otherwise, we're on the right token and our offset is correct because no recombinations occurred
 456  33 else offset = 0;
 457  33 return offset;
 458    }
 459   
 460    /** Checks if the previous token is of a certain type.
 461    * @param delTo the cursor for calling prevItem on
 462    * @param match the type we want to check
 463    * @return true if the previous token is of type match
 464    */
 465  3 private boolean _checkPrevEquals(Iterator delTo, String match) {
 466  0 if (delTo.atFirstItem() || delTo.atStart()) return false;
 467  3 return delTo.prevItem().getType().equals(match);
 468    }
 469   
 470  0 public String toString() { return "" + current(); }
 471   
 472    }
 473    }