|
|||||||||||||||||||
| Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
| ModelList.java | 75% | 100% | 100% | 95.2% |
|
||||||||||||||
| 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 | import edu.rice.cs.plt.collect.WeakHashSet; | |
| 40 | import java.util.Set; | |
| 41 | ||
| 42 | /** A doubly-linked list class with header and trailer nodes. Allows multiple iterators to make modifications to the | |
| 43 | * same list without failing unlike the iterators for java.util.*List. | |
| 44 | * @version $Id: ModelList.java 5175 2010-01-20 08:46:32Z mgricken $ | |
| 45 | */ | |
| 46 | class ModelList<T> { | |
| 47 | private Node<T> _head; | |
| 48 | private Node<T> _tail; | |
| 49 | /** length of this list; supports constant time length lookup */ | |
| 50 | private int _length; | |
| 51 | /** a set of objects that can trigger and listen for updates to the list */ | |
| 52 | private Set<ModelIterator> _listeners; | |
| 53 | ||
| 54 | /** Constructor. Initializes the head and tail nodes, as well as the listener table and the length variable. */ | |
| 55 | 3191 | ModelList() { |
| 56 | // This node is the only node that exists in an empty list. | |
| 57 | // If an Iterator points to this node, the iterator is considered to be in "initial position." | |
| 58 | 3191 | _head = new Node<T>(); |
| 59 | 3191 | _tail = new Node<T>(); |
| 60 | 3191 | _head._prev = null; |
| 61 | 3191 | _head._next = _tail; |
| 62 | 3191 | _tail._prev = _head; |
| 63 | 3191 | _tail._next = null; |
| 64 | 3191 | _length = 0; |
| 65 | ||
| 66 | /* We use a WeakHashSet so that listeners do not leak. That is, even if the dispose method is not called, when they | |
| 67 | * are no longer strongly referenced, they will be automatically removed from the listener set. */ | |
| 68 | 3191 | _listeners = new WeakHashSet<ModelIterator>(); |
| 69 | } | |
| 70 | ||
| 71 | 4 | public void insertFront(T item) { insert(_head._next, item); } |
| 72 | ||
| 73 | /** Insert a node immediately before the specified point. Returns the inserted node. Assumes point is not head. */ | |
| 74 | 14418 | private Node<T> insert(Node<T> point, T item) { |
| 75 | 14418 | assert point != _head; |
| 76 | 14418 | Node<T> newNode = point.insert(item); |
| 77 | 14418 | _length++; |
| 78 | 14418 | return newNode; |
| 79 | } | |
| 80 | ||
| 81 | /** Remove a node from the list. Assumes point is not head or tail. */ | |
| 82 | 478 | private void remove(Node<T> point) { |
| 83 | 478 | assert point != _head && point != _tail; |
| 84 | 478 | point.remove(); |
| 85 | 478 | _length--; |
| 86 | } | |
| 87 | ||
| 88 | 62773 | private void addListener(ModelIterator that) { _listeners.add(that); } |
| 89 | ||
| 90 | 32446 | private void removeListener(ModelIterator that) { _listeners.remove(that); } |
| 91 | ||
| 92 | 5 | public int listenerCount() { return _listeners.size(); } |
| 93 | ||
| 94 | /** Returns true if the list is empty. */ | |
| 95 | 2942167 | public boolean isEmpty() { return _head._next == _tail; } |
| 96 | ||
| 97 | 18 | public int length() { return _length; } |
| 98 | ||
| 99 | /** Create a new iterator for this list and register it as one of the listeners which are notified when the list is | |
| 100 | * updated. | |
| 101 | */ | |
| 102 | 19 | public ModelIterator getIterator() { return new ModelIterator(); } |
| 103 | ||
| 104 | /** The Node class for ModelLists. The _prev and _next pointers are mutable. The _item field is null in _head and _tail. */ | |
| 105 | private static class Node<T> { | |
| 106 | Node<T> _prev; | |
| 107 | Node<T> _next; | |
| 108 | T _item; | |
| 109 | ||
| 110 | /** Constructor for _head and _tail nodes. */ | |
| 111 | 6382 | Node() { } |
| 112 | ||
| 113 | /** Constructor for nodes containing data. */ | |
| 114 | 14418 | Node(T item, Node<T> pred, Node<T> succ) { |
| 115 | 14418 | _item = item; |
| 116 | 14418 | _prev = pred; |
| 117 | 14418 | _next = succ; |
| 118 | } | |
| 119 | ||
| 120 | /** Insert a new node before "this". Returns the new node. Assumes that "this" is not the head node. */ | |
| 121 | 14418 | Node<T> insert(T item) { |
| 122 | 14418 | assert _prev != null; |
| 123 | 14418 | Node<T> newNode = new Node<T>(item, _prev, this); |
| 124 | 14418 | _prev._next = newNode; |
| 125 | 14418 | _prev = newNode; |
| 126 | 14418 | return newNode; |
| 127 | } | |
| 128 | ||
| 129 | /** Remove the current node. Assumes that this is neither _head not _tail. */ | |
| 130 | 478 | void remove() { |
| 131 | 478 | assert _prev != null && _next != null; |
| 132 | 478 | _prev._next = _next; |
| 133 | 478 | _next._prev = _prev; |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | /** The iterator class for ModelList. Package private instead of private so that it can be extended. The methods of | |
| 138 | * this class constitute the only public interface for traversing and modifying ModelList objects (other than | |
| 139 | * insertFront). These iterators support concurrent modification from within the same thread. They are NOT thread | |
| 140 | * safe. | |
| 141 | */ | |
| 142 | class ModelIterator { | |
| 143 | private Node<T> _point; // the current node | |
| 144 | private int _pos; // the offset of _point within the list; _head has index 0 | |
| 145 | ||
| 146 | /** Standard constructor that creates an iterator pointing to the list head (_head) and adds it the listeners. */ | |
| 147 | 3183 | public ModelIterator() { |
| 148 | 3183 | _point = _head; |
| 149 | 3183 | _pos = 0; |
| 150 | 3183 | addListener(this); |
| 151 | } | |
| 152 | ||
| 153 | /** Copy constructor that creates a copy of an existing iterator and adds it to the listeners. */ | |
| 154 | 59590 | public ModelIterator(ModelIterator iter) { |
| 155 | 59590 | _point = iter._point; |
| 156 | 59590 | _pos = iter._pos; |
| 157 | 59590 | addListener(this); |
| 158 | } | |
| 159 | ||
| 160 | 3 | public ModelIterator copy() { return new ModelIterator(this); } |
| 161 | ||
| 162 | /** Tests "that" for equality with "this". */ | |
| 163 | 1164 | public boolean eq(ModelIterator that) { return _point == that._point; } |
| 164 | ||
| 165 | /** Force "this" iterator to take the values of "that". */ | |
| 166 | 490 | public void setTo(ModelIterator that) { |
| 167 | 490 | _point = that._point; |
| 168 | 490 | _pos = that._pos; |
| 169 | } | |
| 170 | ||
| 171 | /** Disposes of an iterator by removing it from the listeners. If an iterator becomes unreachable, it is | |
| 172 | * automatically reclaimed as part of system garbage collection. The manual use of dispose() reduces the | |
| 173 | * cost of notifying the listeners because it reduces the size of the listener set. | |
| 174 | */ | |
| 175 | 32446 | public void dispose() { removeListener(this); } |
| 176 | ||
| 177 | /** Return true if we're pointing at the head.*/ | |
| 178 | 28016605 | public boolean atStart() { return _point == _head; } |
| 179 | ||
| 180 | /** Return true if we're pointing at the tail. */ | |
| 181 | 25170483 | public boolean atEnd() { return _point == _tail; } |
| 182 | ||
| 183 | /** Return true if we're pointing at the node after the head. */ | |
| 184 | 2949750 | public boolean atFirstItem() { return _point._prev == _head; } |
| 185 | ||
| 186 | /** Return true if we're pointing at the node before the tail. */ | |
| 187 | 63588 | public boolean atLastItem() { return _point._next == _tail; } |
| 188 | ||
| 189 | /** Return the item associated with the current node. */ | |
| 190 | 24427096 | public T current() { |
| 191 | // assert ! atStart() && ! atEnd(); | |
| 192 | 24427096 | return _point._item; |
| 193 | } | |
| 194 | ||
| 195 | /** Returns the item associated with the node before the current node. */ | |
| 196 | 2529692 | public T prevItem() { |
| 197 | 2529692 | assert ! atStart() && ! isEmpty() && ! atFirstItem(); |
| 198 | 2529692 | return _point._prev._item; |
| 199 | } | |
| 200 | ||
| 201 | /** Returns the item associated with the node after the current node. */ | |
| 202 | 2 | public T nextItem() { |
| 203 | 2 | assert ! atStart() && ! isEmpty() && ! atLastItem(); |
| 204 | 2 | return _point._next._item; |
| 205 | } | |
| 206 | ||
| 207 | 11 | public int pos() { return _pos; } |
| 208 | ||
| 209 | /** Inserts an item before the current item. If current is head, we need to move to the next node | |
| 210 | * to perform the insert properly. Otherwise, we'll get a null pointer exception because the function will try | |
| 211 | * to insert the new item before the head. Ends pointing to inserted item. | |
| 212 | */ | |
| 213 | 14414 | public void insert(T item) { |
| 214 | //so as not to insert at head | |
| 215 | 1472 | if (atStart()) next(); |
| 216 | 14414 | _point = ModelList.this.insert(_point, item); |
| 217 | 14414 | int savPos = _pos; |
| 218 | 14414 | notifyOfInsert(_pos); |
| 219 | ||
| 220 | 14414 | _pos = savPos; // this._pos is incremented by notify; reverse this change |
| 221 | } | |
| 222 | ||
| 223 | /** Removes the current item from the list. Ends pointing to the node following the removed node. | |
| 224 | * Throws exception if performed atStart() or atEnd(). | |
| 225 | */ | |
| 226 | 478 | public void remove() { |
| 227 | 478 | Node<T> succ = _point._next; |
| 228 | 478 | ModelList.this.remove(_point); |
| 229 | 478 | _point = succ; |
| 230 | 478 | notifyOfRemove(_pos, succ); |
| 231 | } | |
| 232 | ||
| 233 | /** Moves to the previous node. Throws exception atStart(). */ | |
| 234 | 11962563 | public void prev() { |
| 235 | 11962563 | assert ! atStart(); |
| 236 | 11962563 | _point = _point._prev; |
| 237 | 11962563 | _pos--; |
| 238 | } | |
| 239 | ||
| 240 | /** Moves to the next node. Throws exception atEnd(). */ | |
| 241 | 12035403 | public void next() { |
| 242 | 12035403 | assert ! atEnd(); |
| 243 | 12035403 | _point = _point._next; |
| 244 | 12035403 | _pos++; |
| 245 | } | |
| 246 | ||
| 247 | /** Delete all nodes between the current position of this and the current position of the given iterator. | |
| 248 | * 1) Two iterators pointing to same node: do nothing | |
| 249 | * 2) Iterator 2 is before iterator 1: remove between iterator 2 and iterator 1 | |
| 250 | * 3) Iterator 1 is before iterator 2: remove between iterator 1 and iterator 2 | |
| 251 | * Does not remove points iterators point to. | |
| 252 | */ | |
| 253 | 680 | public void collapse(ModelIterator iter) { |
| 254 | 680 | int itPos = iter._pos; |
| 255 | 680 | int diff = Math.abs(_pos - itPos); |
| 256 | 389 | if (diff <= 1) return; // _pos and iter.pos are either equal or adjacent |
| 257 | ||
| 258 | 291 | int leftPos, rightPos; |
| 259 | 291 | Node<T> leftPoint, rightPoint; |
| 260 | ||
| 261 | 291 | if (_pos > itPos) { |
| 262 | 1 | leftPos = itPos; |
| 263 | 1 | leftPoint = iter._point; |
| 264 | 1 | rightPos = _pos; |
| 265 | 1 | rightPoint = _point; |
| 266 | } | |
| 267 | else /* _pos < iter._pos */ { | |
| 268 | 290 | leftPos = _pos; |
| 269 | 290 | leftPoint = _point; |
| 270 | 290 | rightPos = itPos; |
| 271 | 290 | rightPoint = iter._point; |
| 272 | } | |
| 273 | ||
| 274 | 291 | rightPoint._prev = leftPoint; |
| 275 | 291 | leftPoint._next = rightPoint; |
| 276 | 291 | _length -= rightPos - leftPos - 1; //determine new length |
| 277 | 291 | notifyOfCollapse(leftPos, rightPos, rightPoint); |
| 278 | } | |
| 279 | ||
| 280 | /** Notifies the iterators in _listeners that a node has been inserted. */ | |
| 281 | 14414 | private void notifyOfInsert(int pos) { |
| 282 | 14414 | for (ModelIterator listener : _listeners) { |
| 283 | 33138 | int lisPos = listener._pos; |
| 284 | 21799 | if (lisPos >= pos) listener._pos = lisPos + 1; |
| 285 | } | |
| 286 | } | |
| 287 | ||
| 288 | /** Notifies the iterators in _listeners that a node has been removed. */ | |
| 289 | 478 | private void notifyOfRemove(int pos, Node<T> point) { |
| 290 | 478 | for (ModelIterator listener : _listeners) { |
| 291 | 2828 | int lisPos = listener._pos; |
| 292 | 556 | if (lisPos == pos) listener._point = point; |
| 293 | 1403 | else if (lisPos > pos) listener._pos = lisPos - 1; |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | /** Notifies the iterators in _listeners that a range of nodes has been collapsed. */ | |
| 298 | 291 | private void notifyOfCollapse(int leftPos, int rightPos, Node<T> rightPoint) { |
| 299 | 291 | for (ModelIterator listener : _listeners) { |
| 300 | 1946 | int lisPos = listener._pos; |
| 301 | 824 | if (lisPos <= leftPos) continue; |
| 302 | 1122 | if (lisPos < rightPos) { |
| 303 | 343 | listener._pos = leftPos + 1; |
| 304 | 343 | listener._point = rightPoint; |
| 305 | } | |
| 306 | else { // lisPos >+ rightPos | |
| 307 | 779 | listener._pos = lisPos - (rightPos - leftPos - 1); |
| 308 | } | |
| 309 | } | |
| 310 | } | |
| 311 | } | |
| 312 | } |
|
||||||||||