| Index: utils/yaml/yaml_map.dart
|
| diff --git a/utils/yaml/yaml_map.dart b/utils/yaml/yaml_map.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..becae905545e8a05cc101db4e47a2dcc4dc5b5c9
|
| --- /dev/null
|
| +++ b/utils/yaml/yaml_map.dart
|
| @@ -0,0 +1,137 @@
|
| +// Copyright (c) 2012, 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.
|
| +
|
| +/**
|
| + * This class wraps behaves almost identically to the normal Dart Map
|
| + * implementation, with the following differences:
|
| + *
|
| + * * It allows null, NaN, boolean, list, and map keys.
|
| + * * It is itself Hashable.
|
| + * * It defines `==` structurally. That is, `yamlMap1 == yamlMap2` if they have
|
| + * the same contents.
|
| + */
|
| +class YamlMap implements Map, Hashable {
|
| + Map _map;
|
| +
|
| + YamlMap() : _map = new Map();
|
| +
|
| + YamlMap.from(Map map) : _map = new Map.from(map);
|
| +
|
| + bool containsValue(value) => _map.containsValue(value);
|
| + bool containsKey(key) => _map.containsKey(_wrapKey(key));
|
| + operator [](key) => _map[_wrapKey(key)];
|
| + operator []=(key, value) { _map[_wrapKey(key)] = value; }
|
| + putIfAbsent(key, ifAbsent()) => _map.putIfAbsent(_wrapKey(key), ifAbsent);
|
| + remove(key) => _map.remove(_wrapKey(key));
|
| + void clear() => _map.clear();
|
| + void forEach(void f(key, value)) =>
|
| + _map.forEach((k, v) => f(_unwrapKey(k), v));
|
| + Collection getKeys() => _map.getKeys().map(_unwrapKey);
|
| + Collection getValues() => _map.getValues();
|
| + int get length() => _map.length;
|
| + bool isEmpty() => _map.isEmpty();
|
| + String toString() => _map.toString();
|
| +
|
| + int hashCode() => _hashCode(_map);
|
| +
|
| + bool operator ==(other) {
|
| + if (other is! YamlMap) return false;
|
| + return _mapEquals(this, other);
|
| + }
|
| +
|
| + /** Wraps an object for use as a key in the map. */
|
| + _wrapKey(obj) {
|
| + if (obj != null && obj is! bool && obj is! List &&
|
| + (obj is! double || !obj.isNan()) &&
|
| + (obj is! Map || obj is YamlMap)) {
|
| + return obj;
|
| + }
|
| + return new _WrappedHashKey._(obj);
|
| + }
|
| +
|
| + /** Unwraps an object that was used as a key in the map. */
|
| + _unwrapKey(obj) => obj is _WrappedHashKey ? obj.value : obj;
|
| +}
|
| +
|
| +/**
|
| + * A class for wrapping normally-unhashable objects that are being used as keys
|
| + * in a YamlMap.
|
| + */
|
| +class _WrappedHashKey implements Hashable {
|
| + var value;
|
| +
|
| + _WrappedHashKey._(this.value);
|
| +
|
| + int hashCode() => _hashCode(value);
|
| +
|
| + String toString() => value.toString();
|
| +
|
| + /** This is defined as both values being structurally equal. */
|
| + bool operator ==(other) {
|
| + if (other is! _WrappedHashKey) return false;
|
| + _WrappedHashKey otherWrappedKey = other;
|
| + return _deepEquals(this.value, other.value);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Returns the hash code for [obj]. This includes null, true, false, maps, and
|
| + * lists.
|
| + */
|
| +int _hashCode(obj) {
|
| + if (obj == null) return 0;
|
| + if (obj == true) return 1;
|
| + if (obj == false) return 2;
|
| + if (obj is Map) return _hashCode(obj.getKeys()) ^ _hashCode(obj.getValues());
|
| + if (obj is List) {
|
| + // This is probably a really bad hash function, but presumably we'll get this
|
| + // in the standard library before it actually matters.
|
| + int hash = 0;
|
| + for (var e in obj) {
|
| + hash ^= _hashCode(e);
|
| + }
|
| + return hash;
|
| + }
|
| + return obj.hashCode();
|
| +}
|
| +
|
| +/** Returns whether [list1] and [list2] are structurally equal. */
|
| +bool _listEquals(List list1, List list2) {
|
| + if (list1.length != list2.length) return false;
|
| +
|
| + for (var i = 0; i < list1.length; i++) {
|
| + if (!_deepEquals(list1[i], list2[i])) return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +/** Returns whether [map1] and [map2] are structurally equal. */
|
| +bool _mapEquals(Map map1, Map map2) {
|
| + if (map1.length != map2.length) return false;
|
| +
|
| + for (var key in map1.getKeys()) {
|
| + if (!map2.containsKey(key)) return false;
|
| + if (!_deepEquals(map1[key], map2[key])) return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +/**
|
| + * Returns whether two doubles are equivalent. This differs from `d1 == d2` in
|
| + * that it considers NaN to be equal to itself.
|
| + */
|
| +bool _doubleEquals(double d1, double d2) {
|
| + if (d1.isNaN() && d2.isNaN()) return true;
|
| + return d1 == d2;
|
| +}
|
| +
|
| +/** Returns whether two objects are structurally equivalent. */
|
| +bool _deepEquals(obj1, obj2) {
|
| + if (obj1 is List && obj2 is List) return _listEquals(obj1, obj2);
|
| + if (obj1 is Map && obj2 is Map) return _mapEquals(obj1, obj2);
|
| + if (obj1 is double && obj2 is double) return _doubleEquals(obj1, obj2);
|
| + return obj1 == obj2;
|
| +}
|
|
|