OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library engine.utilities.collection; |
| 6 |
| 7 import "dart:math" as math; |
| 8 import 'dart:collection'; |
| 9 |
| 10 import 'java_core.dart'; |
| 11 import 'scanner.dart' show Token; |
| 12 |
| 13 /** |
| 14 * The class `BooleanArray` defines methods for operating on integers as if they
were arrays |
| 15 * of booleans. These arrays can be indexed by either integers or by enumeration
constants. |
| 16 */ |
| 17 class BooleanArray { |
| 18 /** |
| 19 * Return the value of the element at the given index. |
| 20 * |
| 21 * @param array the array being accessed |
| 22 * @param index the index of the element being accessed |
| 23 * @return the value of the element at the given index |
| 24 * @throws IndexOutOfBoundsException if the index is not between zero (0) and
31, inclusive |
| 25 */ |
| 26 static bool get(int array, int index) { |
| 27 _checkIndex(index); |
| 28 return (array & (1 << index)) > 0; |
| 29 } |
| 30 |
| 31 /** |
| 32 * Return the value of the element at the given index. |
| 33 * |
| 34 * @param array the array being accessed |
| 35 * @param index the index of the element being accessed |
| 36 * @return the value of the element at the given index |
| 37 * @throws IndexOutOfBoundsException if the index is not between zero (0) and
31, inclusive |
| 38 */ |
| 39 static bool getEnum(int array, Enum index) => get(array, index.ordinal); |
| 40 |
| 41 /** |
| 42 * Set the value of the element at the given index to the given value. |
| 43 * |
| 44 * @param array the array being modified |
| 45 * @param index the index of the element being set |
| 46 * @param value the value to be assigned to the element |
| 47 * @return the updated value of the array |
| 48 * @throws IndexOutOfBoundsException if the index is not between zero (0) and
31, inclusive |
| 49 */ |
| 50 static int set(int array, int index, bool value) { |
| 51 _checkIndex(index); |
| 52 if (value) { |
| 53 return array | (1 << index); |
| 54 } else { |
| 55 return array & ~(1 << index); |
| 56 } |
| 57 } |
| 58 |
| 59 /** |
| 60 * Set the value of the element at the given index to the given value. |
| 61 * |
| 62 * @param array the array being modified |
| 63 * @param index the index of the element being set |
| 64 * @param value the value to be assigned to the element |
| 65 * @return the updated value of the array |
| 66 * @throws IndexOutOfBoundsException if the index is not between zero (0) and
31, inclusive |
| 67 */ |
| 68 static int setEnum(int array, Enum index, bool value) => |
| 69 set(array, index.ordinal, value); |
| 70 |
| 71 /** |
| 72 * Throw an exception if the index is not within the bounds allowed for an int
eger-encoded array |
| 73 * of boolean values. |
| 74 * |
| 75 * @throws IndexOutOfBoundsException if the index is not between zero (0) and
31, inclusive |
| 76 */ |
| 77 static void _checkIndex(int index) { |
| 78 if (index < 0 || index > 30) { |
| 79 throw new RangeError("Index not between 0 and 30: $index"); |
| 80 } |
| 81 } |
| 82 } |
| 83 |
| 84 /** |
| 85 * Instances of the class `DirectedGraph` implement a directed graph in which th
e nodes are |
| 86 * arbitrary (client provided) objects and edges are represented implicitly. The
graph will allow an |
| 87 * edge from any node to any other node, including itself, but will not represen
t multiple edges |
| 88 * between the same pair of nodes. |
| 89 * |
| 90 * @param N the type of the nodes in the graph |
| 91 */ |
| 92 class DirectedGraph<N> { |
| 93 /** |
| 94 * The table encoding the edges in the graph. An edge is represented by an ent
ry mapping the head |
| 95 * to a set of tails. Nodes that are not the head of any edge are represented
by an entry mapping |
| 96 * the node to an empty set of tails. |
| 97 */ |
| 98 HashMap<N, HashSet<N>> _edges = new HashMap<N, HashSet<N>>(); |
| 99 |
| 100 /** |
| 101 * Return `true` if this graph is empty. |
| 102 * |
| 103 * @return `true` if this graph is empty |
| 104 */ |
| 105 bool get isEmpty => _edges.isEmpty; |
| 106 |
| 107 /** |
| 108 * Return the number of nodes in this graph. |
| 109 * |
| 110 * @return the number of nodes in this graph |
| 111 */ |
| 112 int get nodeCount => _edges.length; |
| 113 |
| 114 /** |
| 115 * Return a set of all nodes in the graph. |
| 116 */ |
| 117 Set<N> get nodes => _edges.keys.toSet(); |
| 118 |
| 119 /** |
| 120 * Add an edge from the given head node to the given tail node. Both nodes wil
l be a part of the |
| 121 * graph after this method is invoked, whether or not they were before. |
| 122 * |
| 123 * @param head the node at the head of the edge |
| 124 * @param tail the node at the tail of the edge |
| 125 */ |
| 126 void addEdge(N head, N tail) { |
| 127 // |
| 128 // First, ensure that the tail is a node known to the graph. |
| 129 // |
| 130 if (_edges[tail] == null) { |
| 131 _edges[tail] = new HashSet<N>(); |
| 132 } |
| 133 // |
| 134 // Then create the edge. |
| 135 // |
| 136 HashSet<N> tails = _edges[head]; |
| 137 if (tails == null) { |
| 138 tails = new HashSet<N>(); |
| 139 _edges[head] = tails; |
| 140 } |
| 141 tails.add(tail); |
| 142 } |
| 143 |
| 144 /** |
| 145 * Add the given node to the set of nodes in the graph. |
| 146 * |
| 147 * @param node the node to be added |
| 148 */ |
| 149 void addNode(N node) { |
| 150 HashSet<N> tails = _edges[node]; |
| 151 if (tails == null) { |
| 152 _edges[node] = new HashSet<N>(); |
| 153 } |
| 154 } |
| 155 |
| 156 /** |
| 157 * Run a topological sort of the graph. Since the graph may contain cycles, th
is results in a list |
| 158 * of strongly connected components rather than a list of nodes. The nodes in
each strongly |
| 159 * connected components only have edges that point to nodes in the same compon
ent or earlier |
| 160 * components. |
| 161 */ |
| 162 List<List<N>> computeTopologicalSort() { |
| 163 DirectedGraph_SccFinder<N> finder = new DirectedGraph_SccFinder<N>(this); |
| 164 return finder.computeTopologicalSort(); |
| 165 } |
| 166 |
| 167 /** |
| 168 * Return true if the graph contains at least one path from `source` to `desti
nation`. |
| 169 */ |
| 170 bool containsPath(N source, N destination) { |
| 171 HashSet<N> nodesVisited = new HashSet<N>(); |
| 172 return _containsPathInternal(source, destination, nodesVisited); |
| 173 } |
| 174 |
| 175 /** |
| 176 * Return a list of nodes that form a cycle containing the given node. If the
node is not part of |
| 177 * this graph, then a list containing only the node itself will be returned. |
| 178 * |
| 179 * @return a list of nodes that form a cycle containing the given node |
| 180 */ |
| 181 List<N> findCycleContaining(N node) { |
| 182 if (node == null) { |
| 183 throw new IllegalArgumentException(); |
| 184 } |
| 185 DirectedGraph_SccFinder<N> finder = new DirectedGraph_SccFinder<N>(this); |
| 186 return finder.componentContaining(node); |
| 187 } |
| 188 |
| 189 /** |
| 190 * Return a set containing the tails of edges that have the given node as thei
r head. The set will |
| 191 * be empty if there are no such edges or if the node is not part of the graph
. Clients must not |
| 192 * modify the returned set. |
| 193 * |
| 194 * @param head the node at the head of all of the edges whose tails are to be
returned |
| 195 * @return a set containing the tails of edges that have the given node as the
ir head |
| 196 */ |
| 197 Set<N> getTails(N head) { |
| 198 HashSet<N> tails = _edges[head]; |
| 199 if (tails == null) { |
| 200 return new HashSet<N>(); |
| 201 } |
| 202 return tails; |
| 203 } |
| 204 |
| 205 /** |
| 206 * Remove all of the given nodes from this graph. As a consequence, any edges
for which those |
| 207 * nodes were either a head or a tail will also be removed. |
| 208 * |
| 209 * @param nodes the nodes to be removed |
| 210 */ |
| 211 void removeAllNodes(List<N> nodes) { |
| 212 for (N node in nodes) { |
| 213 removeNode(node); |
| 214 } |
| 215 } |
| 216 |
| 217 /** |
| 218 * Remove the edge from the given head node to the given tail node. If there w
as no such edge then |
| 219 * the graph will be unmodified: the number of edges will be the same and the
set of nodes will be |
| 220 * the same (neither node will either be added or removed). |
| 221 * |
| 222 * @param head the node at the head of the edge |
| 223 * @param tail the node at the tail of the edge |
| 224 * @return `true` if the graph was modified as a result of this operation |
| 225 */ |
| 226 void removeEdge(N head, N tail) { |
| 227 HashSet<N> tails = _edges[head]; |
| 228 if (tails != null) { |
| 229 tails.remove(tail); |
| 230 } |
| 231 } |
| 232 |
| 233 /** |
| 234 * Remove the given node from this graph. As a consequence, any edges for whic
h that node was |
| 235 * either a head or a tail will also be removed. |
| 236 * |
| 237 * @param node the node to be removed |
| 238 */ |
| 239 void removeNode(N node) { |
| 240 _edges.remove(node); |
| 241 for (HashSet<N> tails in _edges.values) { |
| 242 tails.remove(node); |
| 243 } |
| 244 } |
| 245 |
| 246 /** |
| 247 * Find one node (referred to as a sink node) that has no outgoing edges (that
is, for which there |
| 248 * are no edges that have that node as the head of the edge) and remove it fro
m this graph. Return |
| 249 * the node that was removed, or `null` if there are no such nodes either beca
use the graph |
| 250 * is empty or because every node in the graph has at least one outgoing edge.
As a consequence of |
| 251 * removing the node from the graph any edges for which that node was a tail w
ill also be removed. |
| 252 * |
| 253 * @return the sink node that was removed |
| 254 */ |
| 255 N removeSink() { |
| 256 N sink = _findSink(); |
| 257 if (sink == null) { |
| 258 return null; |
| 259 } |
| 260 removeNode(sink); |
| 261 return sink; |
| 262 } |
| 263 |
| 264 bool _containsPathInternal(N source, N destination, HashSet<N> nodesVisited) { |
| 265 if (identical(source, destination)) { |
| 266 return true; |
| 267 } |
| 268 HashSet<N> tails = _edges[source]; |
| 269 if (tails != null) { |
| 270 nodesVisited.add(source); |
| 271 for (N tail in tails) { |
| 272 if (!nodesVisited.contains(tail)) { |
| 273 if (_containsPathInternal(tail, destination, nodesVisited)) { |
| 274 return true; |
| 275 } |
| 276 } |
| 277 } |
| 278 } |
| 279 return false; |
| 280 } |
| 281 |
| 282 /** |
| 283 * Return one node that has no outgoing edges (that is, for which there are no
edges that have |
| 284 * that node as the head of the edge), or `null` if there are no such nodes. |
| 285 * |
| 286 * @return a sink node |
| 287 */ |
| 288 N _findSink() { |
| 289 for (N key in _edges.keys) { |
| 290 if (_edges[key].isEmpty) return key; |
| 291 } |
| 292 return null; |
| 293 } |
| 294 } |
| 295 |
| 296 /** |
| 297 * Instances of the class `NodeInfo` are used by the [SccFinder] to maintain |
| 298 * information about the nodes that have been examined. |
| 299 * |
| 300 * @param N the type of the nodes corresponding to the entries |
| 301 */ |
| 302 class DirectedGraph_NodeInfo<N> { |
| 303 /** |
| 304 * The depth of this node. |
| 305 */ |
| 306 int index = 0; |
| 307 |
| 308 /** |
| 309 * The depth of the first node in a cycle. |
| 310 */ |
| 311 int lowlink = 0; |
| 312 |
| 313 /** |
| 314 * A flag indicating whether the corresponding node is on the stack. Used to r
emove the need for |
| 315 * searching a collection for the node each time the question needs to be aske
d. |
| 316 */ |
| 317 bool onStack = false; |
| 318 |
| 319 /** |
| 320 * The component that contains the corresponding node. |
| 321 */ |
| 322 List<N> component; |
| 323 |
| 324 /** |
| 325 * Initialize a newly created information holder to represent a node at the gi
ven depth. |
| 326 * |
| 327 * @param depth the depth of the node being represented |
| 328 */ |
| 329 DirectedGraph_NodeInfo(int depth) { |
| 330 index = depth; |
| 331 lowlink = depth; |
| 332 onStack = false; |
| 333 } |
| 334 } |
| 335 |
| 336 /** |
| 337 * Instances of the class `SccFinder` implement Tarjan's Algorithm for finding t
he strongly |
| 338 * connected components in a graph. |
| 339 */ |
| 340 class DirectedGraph_SccFinder<N> { |
| 341 /** |
| 342 * The graph to work with. |
| 343 */ |
| 344 final DirectedGraph<N> _graph; |
| 345 |
| 346 /** |
| 347 * The index used to uniquely identify the depth of nodes. |
| 348 */ |
| 349 int _index = 0; |
| 350 |
| 351 /** |
| 352 * The stack of nodes that are being visited in order to identify components. |
| 353 */ |
| 354 List<N> _stack = new List<N>(); |
| 355 |
| 356 /** |
| 357 * A table mapping nodes to information about the nodes that is used by this a
lgorithm. |
| 358 */ |
| 359 HashMap<N, DirectedGraph_NodeInfo<N>> _nodeMap = |
| 360 new HashMap<N, DirectedGraph_NodeInfo<N>>(); |
| 361 |
| 362 /** |
| 363 * A list of all strongly connected components found, in topological sort orde
r (each node in a |
| 364 * strongly connected component only has edges that point to nodes in the same
component or |
| 365 * earlier components). |
| 366 */ |
| 367 List<List<N>> _allComponents = new List<List<N>>(); |
| 368 |
| 369 /** |
| 370 * Initialize a newly created finder. |
| 371 */ |
| 372 DirectedGraph_SccFinder(this._graph) : super(); |
| 373 |
| 374 /** |
| 375 * Return a list containing the nodes that are part of the strongly connected
component that |
| 376 * contains the given node. |
| 377 * |
| 378 * @param node the node used to identify the strongly connected component to b
e returned |
| 379 * @return the nodes that are part of the strongly connected component that co
ntains the given |
| 380 * node |
| 381 */ |
| 382 List<N> componentContaining(N node) => _strongConnect(node).component; |
| 383 |
| 384 /** |
| 385 * Run Tarjan's algorithm and return the resulting list of strongly connected
components. The |
| 386 * list is in topological sort order (each node in a strongly connected compon
ent only has edges |
| 387 * that point to nodes in the same component or earlier components). |
| 388 */ |
| 389 List<List<N>> computeTopologicalSort() { |
| 390 for (N node in _graph._edges.keys.toSet()) { |
| 391 DirectedGraph_NodeInfo<N> nodeInfo = _nodeMap[node]; |
| 392 if (nodeInfo == null) { |
| 393 _strongConnect(node); |
| 394 } |
| 395 } |
| 396 return _allComponents; |
| 397 } |
| 398 |
| 399 /** |
| 400 * Remove and return the top-most element from the stack. |
| 401 * |
| 402 * @return the element that was removed |
| 403 */ |
| 404 N _pop() { |
| 405 N node = _stack.removeAt(_stack.length - 1); |
| 406 _nodeMap[node].onStack = false; |
| 407 return node; |
| 408 } |
| 409 |
| 410 /** |
| 411 * Add the given node to the stack. |
| 412 * |
| 413 * @param node the node to be added to the stack |
| 414 */ |
| 415 void _push(N node) { |
| 416 _nodeMap[node].onStack = true; |
| 417 _stack.add(node); |
| 418 } |
| 419 |
| 420 /** |
| 421 * Compute the strongly connected component that contains the given node as we
ll as any |
| 422 * components containing nodes that are reachable from the given component. |
| 423 * |
| 424 * @param v the node from which the search will begin |
| 425 * @return the information about the given node |
| 426 */ |
| 427 DirectedGraph_NodeInfo<N> _strongConnect(N v) { |
| 428 // |
| 429 // Set the depth index for v to the smallest unused index |
| 430 // |
| 431 DirectedGraph_NodeInfo<N> vInfo = new DirectedGraph_NodeInfo<N>(_index++); |
| 432 _nodeMap[v] = vInfo; |
| 433 _push(v); |
| 434 // |
| 435 // Consider successors of v |
| 436 // |
| 437 HashSet<N> tails = _graph._edges[v]; |
| 438 if (tails != null) { |
| 439 for (N w in tails) { |
| 440 DirectedGraph_NodeInfo<N> wInfo = _nodeMap[w]; |
| 441 if (wInfo == null) { |
| 442 // Successor w has not yet been visited; recurse on it |
| 443 wInfo = _strongConnect(w); |
| 444 vInfo.lowlink = math.min(vInfo.lowlink, wInfo.lowlink); |
| 445 } else if (wInfo.onStack) { |
| 446 // Successor w is in stack S and hence in the current SCC |
| 447 vInfo.lowlink = math.min(vInfo.lowlink, wInfo.index); |
| 448 } |
| 449 } |
| 450 } |
| 451 // |
| 452 // If v is a root node, pop the stack and generate an SCC |
| 453 // |
| 454 if (vInfo.lowlink == vInfo.index) { |
| 455 List<N> component = new List<N>(); |
| 456 N w; |
| 457 do { |
| 458 w = _pop(); |
| 459 component.add(w); |
| 460 _nodeMap[w].component = component; |
| 461 } while (!identical(w, v)); |
| 462 _allComponents.add(component); |
| 463 } |
| 464 return vInfo; |
| 465 } |
| 466 } |
| 467 |
| 468 /** |
| 469 * The class `ListUtilities` defines utility methods useful for working with [Li
st |
| 470 ]. |
| 471 */ |
| 472 class ListUtilities { |
| 473 /** |
| 474 * Add all of the elements in the given array to the given list. |
| 475 * |
| 476 * @param list the list to which the elements are to be added |
| 477 * @param elements the elements to be added to the list |
| 478 */ |
| 479 static void addAll(List list, List<Object> elements) { |
| 480 int count = elements.length; |
| 481 for (int i = 0; i < count; i++) { |
| 482 list.add(elements[i]); |
| 483 } |
| 484 } |
| 485 } |
| 486 |
| 487 /** |
| 488 * The interface `MapIterator` defines the behavior of objects that iterate over
the entries |
| 489 * in a map. |
| 490 * |
| 491 * This interface defines the concept of a current entry and provides methods to
access the key and |
| 492 * value in the current entry. When an iterator is first created it will be posi
tioned before the |
| 493 * first entry and there is no current entry until [moveNext] is invoked. When a
ll of the |
| 494 * entries have been accessed there will also be no current entry. |
| 495 * |
| 496 * There is no guarantee made about the order in which the entries are accessibl
e. |
| 497 */ |
| 498 abstract class MapIterator<K, V> { |
| 499 /** |
| 500 * Return the key associated with the current element. |
| 501 * |
| 502 * @return the key associated with the current element |
| 503 * @throws NoSuchElementException if there is no current element |
| 504 */ |
| 505 K get key; |
| 506 |
| 507 /** |
| 508 * Return the value associated with the current element. |
| 509 * |
| 510 * @return the value associated with the current element |
| 511 * @throws NoSuchElementException if there is no current element |
| 512 */ |
| 513 V get value; |
| 514 |
| 515 /** |
| 516 * Set the value associated with the current element to the given value. |
| 517 * |
| 518 * @param newValue the new value to be associated with the current element |
| 519 * @throws NoSuchElementException if there is no current element |
| 520 */ |
| 521 void set value(V newValue); |
| 522 |
| 523 /** |
| 524 * Advance to the next entry in the map. Return `true` if there is a current e
lement that |
| 525 * can be accessed after this method returns. It is safe to invoke this method
even if the |
| 526 * previous invocation returned `false`. |
| 527 * |
| 528 * @return `true` if there is a current element that can be accessed |
| 529 */ |
| 530 bool moveNext(); |
| 531 } |
| 532 |
| 533 /** |
| 534 * Instances of the class `MultipleMapIterator` implement an iterator that can b
e used to |
| 535 * sequentially access the entries in multiple maps. |
| 536 */ |
| 537 class MultipleMapIterator<K, V> implements MapIterator<K, V> { |
| 538 /** |
| 539 * The iterators used to access the entries. |
| 540 */ |
| 541 List<MapIterator<K, V>> _iterators; |
| 542 |
| 543 /** |
| 544 * The index of the iterator currently being used to access the entries. |
| 545 */ |
| 546 int _iteratorIndex = -1; |
| 547 |
| 548 /** |
| 549 * The current iterator, or `null` if there is no current iterator. |
| 550 */ |
| 551 MapIterator<K, V> _currentIterator; |
| 552 |
| 553 /** |
| 554 * Initialize a newly created iterator to return the entries from the given ma
ps. |
| 555 * |
| 556 * @param maps the maps containing the entries to be iterated |
| 557 */ |
| 558 MultipleMapIterator(List<Map<K, V>> maps) { |
| 559 int count = maps.length; |
| 560 _iterators = new List<MapIterator<K, V>>(count); |
| 561 for (int i = 0; i < count; i++) { |
| 562 _iterators[i] = new SingleMapIterator<K, V>(maps[i]); |
| 563 } |
| 564 } |
| 565 |
| 566 @override |
| 567 K get key { |
| 568 if (_currentIterator == null) { |
| 569 throw new NoSuchElementException(); |
| 570 } |
| 571 return _currentIterator.key; |
| 572 } |
| 573 |
| 574 @override |
| 575 V get value { |
| 576 if (_currentIterator == null) { |
| 577 throw new NoSuchElementException(); |
| 578 } |
| 579 return _currentIterator.value; |
| 580 } |
| 581 |
| 582 @override |
| 583 void set value(V newValue) { |
| 584 if (_currentIterator == null) { |
| 585 throw new NoSuchElementException(); |
| 586 } |
| 587 _currentIterator.value = newValue; |
| 588 } |
| 589 |
| 590 @override |
| 591 bool moveNext() { |
| 592 if (_iteratorIndex < 0) { |
| 593 if (_iterators.length == 0) { |
| 594 _currentIterator = null; |
| 595 return false; |
| 596 } |
| 597 if (_advanceToNextIterator()) { |
| 598 return true; |
| 599 } else { |
| 600 _currentIterator = null; |
| 601 return false; |
| 602 } |
| 603 } |
| 604 if (_currentIterator.moveNext()) { |
| 605 return true; |
| 606 } else if (_advanceToNextIterator()) { |
| 607 return true; |
| 608 } else { |
| 609 _currentIterator = null; |
| 610 return false; |
| 611 } |
| 612 } |
| 613 |
| 614 /** |
| 615 * Under the assumption that there are no more entries that can be returned us
ing the current |
| 616 * iterator, advance to the next iterator that has entries. |
| 617 * |
| 618 * @return `true` if there is a current iterator that has entries |
| 619 */ |
| 620 bool _advanceToNextIterator() { |
| 621 _iteratorIndex++; |
| 622 while (_iteratorIndex < _iterators.length) { |
| 623 MapIterator<K, V> iterator = _iterators[_iteratorIndex]; |
| 624 if (iterator.moveNext()) { |
| 625 _currentIterator = iterator; |
| 626 return true; |
| 627 } |
| 628 _iteratorIndex++; |
| 629 } |
| 630 return false; |
| 631 } |
| 632 } |
| 633 |
| 634 /** |
| 635 * Instances of the class `SingleMapIterator` implement an iterator that can be
used to access |
| 636 * the entries in a single map. |
| 637 */ |
| 638 class SingleMapIterator<K, V> implements MapIterator<K, V> { |
| 639 /** |
| 640 * The [Map] containing the entries to be iterated over. |
| 641 */ |
| 642 final Map<K, V> _map; |
| 643 |
| 644 /** |
| 645 * The iterator used to access the entries. |
| 646 */ |
| 647 Iterator<K> _keyIterator; |
| 648 |
| 649 /** |
| 650 * The current key, or `null` if there is no current key. |
| 651 */ |
| 652 K _currentKey; |
| 653 |
| 654 /** |
| 655 * The current value. |
| 656 */ |
| 657 V _currentValue; |
| 658 |
| 659 /** |
| 660 * Initialize a newly created iterator to return the entries from the given ma
p. |
| 661 * |
| 662 * @param map the map containing the entries to be iterated over |
| 663 */ |
| 664 SingleMapIterator(this._map) { |
| 665 this._keyIterator = _map.keys.iterator; |
| 666 } |
| 667 |
| 668 @override |
| 669 K get key { |
| 670 if (_currentKey == null) { |
| 671 throw new NoSuchElementException(); |
| 672 } |
| 673 return _currentKey; |
| 674 } |
| 675 |
| 676 @override |
| 677 V get value { |
| 678 if (_currentKey == null) { |
| 679 throw new NoSuchElementException(); |
| 680 } |
| 681 if (_currentValue == null) { |
| 682 _currentValue = _map[_currentKey]; |
| 683 } |
| 684 return _currentValue; |
| 685 } |
| 686 |
| 687 @override |
| 688 void set value(V newValue) { |
| 689 if (_currentKey == null) { |
| 690 throw new NoSuchElementException(); |
| 691 } |
| 692 _currentValue = newValue; |
| 693 _map[_currentKey] = newValue; |
| 694 } |
| 695 |
| 696 @override |
| 697 bool moveNext() { |
| 698 if (_keyIterator.moveNext()) { |
| 699 _currentKey = _keyIterator.current; |
| 700 _currentValue = null; |
| 701 return true; |
| 702 } else { |
| 703 _currentKey = null; |
| 704 return false; |
| 705 } |
| 706 } |
| 707 |
| 708 /** |
| 709 * Returns a new [SingleMapIterator] instance for the given [Map]. |
| 710 */ |
| 711 static SingleMapIterator forMap(Map map) => new SingleMapIterator(map); |
| 712 } |
| 713 |
| 714 /** |
| 715 * Instances of the class `TokenMap` map one set of tokens to another set of tok
ens. |
| 716 */ |
| 717 class TokenMap { |
| 718 /** |
| 719 * A table mapping tokens to tokens. This should be replaced by a more perform
ant implementation. |
| 720 * One possibility is a pair of parallel arrays, with keys being sorted by the
ir offset and a |
| 721 * cursor indicating where to start searching. |
| 722 */ |
| 723 HashMap<Token, Token> _map = new HashMap<Token, Token>(); |
| 724 |
| 725 /** |
| 726 * Return the token that is mapped to the given token, or `null` if there is n
o token |
| 727 * corresponding to the given token. |
| 728 * |
| 729 * @param key the token being mapped to another token |
| 730 * @return the token that is mapped to the given token |
| 731 */ |
| 732 Token get(Token key) => _map[key]; |
| 733 |
| 734 /** |
| 735 * Map the key to the value. |
| 736 * |
| 737 * @param key the token being mapped to the value |
| 738 * @param value the token to which the key will be mapped |
| 739 */ |
| 740 void put(Token key, Token value) { |
| 741 _map[key] = value; |
| 742 } |
| 743 } |
OLD | NEW |