blob: 65dd11994f04684fa039b946fd65d1d861455534 [file] [log] [blame]
// 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.mappedBy(_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();
}
}