9b23dff467
This reverts commit r17907. TBR. Review URL: https://codereview.chromium.org//12090093 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@17918 260f80e4-7a28-3924-810f-c04153c831b5
111 lines
3.3 KiB
Dart
111 lines
3.3 KiB
Dart
// 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.
|
|
|
|
part of yaml;
|
|
|
|
/// 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 defines `==` structurally. That is, `yamlMap1 == yamlMap2` if they
|
|
/// have the same contents.
|
|
/// * It has a compatible [hashCode] method.
|
|
class YamlMap implements Map {
|
|
Map _map;
|
|
|
|
YamlMap() : _map = new Map();
|
|
|
|
YamlMap.from(Map map) : _map = new Map.from(map);
|
|
|
|
YamlMap._wrap(this._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));
|
|
Iterable get keys => _map.keys.map(_unwrapKey);
|
|
Iterable get values => _map.values;
|
|
int get length => _map.length;
|
|
bool get isEmpty => _map.isEmpty;
|
|
String toString() => _map.toString();
|
|
|
|
int get hashCode => _hashCode(_map);
|
|
|
|
bool operator ==(other) {
|
|
if (other is! YamlMap) return false;
|
|
return deepEquals(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;
|
|
} else if (obj is Map) {
|
|
return new YamlMap._wrap(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 {
|
|
var value;
|
|
|
|
_WrappedHashKey(this.value);
|
|
|
|
int get 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;
|
|
return deepEquals(this.value, other.value);
|
|
}
|
|
}
|
|
|
|
/// Returns the hash code for [obj]. This includes null, true, false, maps, and
|
|
/// lists. Also handles self-referential structures.
|
|
int _hashCode(obj, [List parents]) {
|
|
if (parents == null) {
|
|
parents = [];
|
|
} else if (parents.any((p) => identical(p, obj))) {
|
|
return -1;
|
|
}
|
|
|
|
parents.add(obj);
|
|
try {
|
|
if (obj == null) return 0;
|
|
if (obj == true) return 1;
|
|
if (obj == false) return 2;
|
|
if (obj is Map) {
|
|
return _hashCode(obj.keys, parents) ^
|
|
_hashCode(obj.values, parents);
|
|
}
|
|
if (obj is Iterable) {
|
|
// 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, parents);
|
|
}
|
|
return hash;
|
|
}
|
|
return obj.hashCode;
|
|
} finally {
|
|
parents.removeLast();
|
|
}
|
|
}
|