blob: a43d31c823ea6adc2f293ecc1cf2b1e90b2e252c [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:collection';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/src/generated/java_core.dart';
/**
* Returns `true` if a and b contain equal elements in the same order.
*/
bool listsEqual(List a, List b) {
// TODO(rnystrom): package:collection also implements this, and analyzer
// already transitively depends on that package. Consider using it instead.
if (identical(a, b)) {
return true;
}
if (a.length != b.length) {
return false;
}
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
/**
* Methods for operating on integers as if they were arrays of booleans. These
* arrays can be indexed by either integers or by enumeration constants.
*/
class BooleanArray {
/**
* Return the value of the element of the given [array] at the given [index].
*/
static bool get(int array, int index) {
_checkIndex(index);
return (array & (1 << index)) > 0;
}
/**
* Return the value of the element at the given index.
*/
@deprecated
static bool getEnum<E extends Enum<E>>(int array, Enum<E> index) =>
get(array, index.ordinal);
/**
* Set the value of the element of the given [array] at the given [index] to
* the given [value].
*/
static int set(int array, int index, bool value) {
_checkIndex(index);
if (value) {
return array | (1 << index);
} else {
return array & ~(1 << index);
}
}
/**
* Set the value of the element at the given index to the given value.
*/
@deprecated
static int setEnum<E extends Enum<E>>(int array, Enum<E> index, bool value) =>
set(array, index.ordinal, value);
/**
* Throw an exception if the index is not within the bounds allowed for an
* integer-encoded array of boolean values.
*/
static void _checkIndex(int index) {
if (index < 0 || index > 30) {
throw new RangeError("Index not between 0 and 30: $index");
}
}
}
/**
* The interface `MapIterator` defines the behavior of objects that iterate over the entries
* in a map.
*
* This interface defines the concept of a current entry and provides methods to access the key and
* value in the current entry. When an iterator is first created it will be positioned before the
* first entry and there is no current entry until [moveNext] is invoked. When all of the
* entries have been accessed there will also be no current entry.
*
* There is no guarantee made about the order in which the entries are accessible.
*/
abstract class MapIterator<K, V> {
/**
* Return the key associated with the current element.
*
* @return the key associated with the current element
* @throws NoSuchElementException if there is no current element
*/
K get key;
/**
* Return the value associated with the current element.
*
* @return the value associated with the current element
* @throws NoSuchElementException if there is no current element
*/
V get value;
/**
* Set the value associated with the current element to the given value.
*
* @param newValue the new value to be associated with the current element
* @throws NoSuchElementException if there is no current element
*/
void set value(V newValue);
/**
* Advance to the next entry in the map. Return `true` if there is a current element that
* can be accessed after this method returns. It is safe to invoke this method even if the
* previous invocation returned `false`.
*
* @return `true` if there is a current element that can be accessed
*/
bool moveNext();
}
/**
* Instances of the class `MultipleMapIterator` implement an iterator that can be used to
* sequentially access the entries in multiple maps.
*/
class MultipleMapIterator<K, V> implements MapIterator<K, V> {
/**
* The iterators used to access the entries.
*/
List<MapIterator<K, V>> _iterators;
/**
* The index of the iterator currently being used to access the entries.
*/
int _iteratorIndex = -1;
/**
* The current iterator, or `null` if there is no current iterator.
*/
MapIterator<K, V> _currentIterator;
/**
* Initialize a newly created iterator to return the entries from the given maps.
*
* @param maps the maps containing the entries to be iterated
*/
MultipleMapIterator(List<Map<K, V>> maps) {
int count = maps.length;
_iterators = new List<MapIterator<K, V>>(count);
for (int i = 0; i < count; i++) {
_iterators[i] = new SingleMapIterator<K, V>(maps[i]);
}
}
@override
K get key {
if (_currentIterator == null) {
throw new StateError('No element');
}
return _currentIterator.key;
}
@override
V get value {
if (_currentIterator == null) {
throw new StateError('No element');
}
return _currentIterator.value;
}
@override
void set value(V newValue) {
if (_currentIterator == null) {
throw new StateError('No element');
}
_currentIterator.value = newValue;
}
@override
bool moveNext() {
if (_iteratorIndex < 0) {
if (_iterators.length == 0) {
_currentIterator = null;
return false;
}
if (_advanceToNextIterator()) {
return true;
} else {
_currentIterator = null;
return false;
}
}
if (_currentIterator.moveNext()) {
return true;
} else if (_advanceToNextIterator()) {
return true;
} else {
_currentIterator = null;
return false;
}
}
/**
* Under the assumption that there are no more entries that can be returned using the current
* iterator, advance to the next iterator that has entries.
*
* @return `true` if there is a current iterator that has entries
*/
bool _advanceToNextIterator() {
_iteratorIndex++;
while (_iteratorIndex < _iterators.length) {
MapIterator<K, V> iterator = _iterators[_iteratorIndex];
if (iterator.moveNext()) {
_currentIterator = iterator;
return true;
}
_iteratorIndex++;
}
return false;
}
}
/**
* Instances of the class `SingleMapIterator` implement an iterator that can be used to access
* the entries in a single map.
*/
class SingleMapIterator<K, V> implements MapIterator<K, V> {
/**
* The [Map] containing the entries to be iterated over.
*/
final Map<K, V> _map;
/**
* The iterator used to access the entries.
*/
Iterator<K> _keyIterator;
/**
* The current key, or `null` if there is no current key.
*/
K _currentKey;
/**
* The current value.
*/
V _currentValue;
/**
* Initialize a newly created iterator to return the entries from the given map.
*
* @param map the map containing the entries to be iterated over
*/
SingleMapIterator(this._map) {
this._keyIterator = _map.keys.iterator;
}
@override
K get key {
if (_currentKey == null) {
throw new StateError('No element');
}
return _currentKey;
}
@override
V get value {
if (_currentKey == null) {
throw new StateError('No element');
}
if (_currentValue == null) {
_currentValue = _map[_currentKey];
}
return _currentValue;
}
@override
void set value(V newValue) {
if (_currentKey == null) {
throw new StateError('No element');
}
_currentValue = newValue;
_map[_currentKey] = newValue;
}
@override
bool moveNext() {
if (_keyIterator.moveNext()) {
_currentKey = _keyIterator.current;
_currentValue = null;
return true;
} else {
_currentKey = null;
return false;
}
}
/**
* Returns a new [SingleMapIterator] instance for the given [Map].
*/
static SingleMapIterator forMap(Map map) => new SingleMapIterator(map);
}
/**
* Instances of the class `TokenMap` map one set of tokens to another set of tokens.
*/
class TokenMap {
/**
* A table mapping tokens to tokens. This should be replaced by a more performant implementation.
* One possibility is a pair of parallel arrays, with keys being sorted by their offset and a
* cursor indicating where to start searching.
*/
Map<Token, Token> _map = new HashMap<Token, Token>();
/**
* Return the token that is mapped to the given token, or `null` if there is no token
* corresponding to the given token.
*
* @param key the token being mapped to another token
* @return the token that is mapped to the given token
*/
Token get(Token key) => _map[key];
/**
* Map the key to the value.
*
* @param key the token being mapped to the value
* @param value the token to which the key will be mapped
*/
void put(Token key, Token value) {
_map[key] = value;
}
}