// Copyright (c) 2017, Google Inc. 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 'package:collection/collection.dart'; /// A JSON value. /// /// This class is suitable for use in built_value fields. When serialized it /// maps directly onto JSON values. /// /// Deep operator== and hashCode are provided, meaning the contents of a /// List or Map is used for equality and hashing. /// /// List and Map classes are wrapped in [UnmodifiableListView] and /// [UnmodifiableMapView] so they won't be modifiable via this object. You /// must ensure that no updates are made via the original reference, as a /// copy is not made. /// /// Note: this is an experimental feature. API may change without a major /// version increase. abstract class JsonObject { /// The value, which may be a bool, a List, a Map, a num or a String. Object get value; /// Whether the value is a [bool]. bool get isBool => false; /// The value as a [bool], or throw if not. bool get asBool => throw StateError('Not a bool.'); /// Whether the value is a [List]. bool get isList => false; /// The value as a [List], or throw if not. List get asList => throw StateError('Not a List.'); /// Whether the value is a [Map]. bool get isMap => false; /// The value as a [Map], or throw if not. Map get asMap => throw StateError('Not a Map.'); /// Whether the value is a [num]. bool get isNum => false; /// The value as a [num], or throw if not. num get asNum => throw StateError('Not a num.'); /// Whether the value is a [String]. bool get isString => false; /// The value as a [String], or throw if not. String get asString => throw StateError('Not a String.'); /// Instantiates with [value], which must be a bool, a List, a Map, a num /// or a String. Otherwise, an [ArgumentError] is thrown. factory JsonObject(Object? value) { if (value is num) { return NumJsonObject(value); } else if (value is String) { return StringJsonObject(value); } else if (value is bool) { return BoolJsonObject(value); } else if (value is List) { return ListJsonObject(value); } else if (value is Map) { return MapJsonObject(value); } else if (value is Map) { // Allow wrong type map, check individual values. return MapJsonObject(value.cast()); } else { throw ArgumentError.value(value, 'value', 'Must be bool, List, Map, num or String'); } } JsonObject._(); @override String toString() { return value.toString(); } } /// A [JsonObject] holding a bool. class BoolJsonObject extends JsonObject { @override final bool value; BoolJsonObject(this.value) : super._(); @override bool get isBool => true; @override bool get asBool => value; @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! BoolJsonObject) return false; return value == other.value; } @override int get hashCode => value.hashCode; } /// A [JsonObject] holding a List. class ListJsonObject extends JsonObject { @override final List value; ListJsonObject(List value) : value = UnmodifiableListView(value), super._(); @override bool get isList => true; @override List get asList => value; @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! ListJsonObject) return false; return const DeepCollectionEquality().equals(value, other.value); } @override int get hashCode => const DeepCollectionEquality().hash(value); } /// A [JsonObject] holding a Map. class MapJsonObject extends JsonObject { @override final Map value; MapJsonObject(Map value) : value = UnmodifiableMapView(value), super._(); @override bool get isMap => true; @override Map get asMap => value; @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! MapJsonObject) return false; return const DeepCollectionEquality().equals(value, other.value); } @override int get hashCode => const DeepCollectionEquality().hash(value); } /// A [JsonObject] holding a num. class NumJsonObject extends JsonObject { @override final num value; NumJsonObject(this.value) : super._(); @override bool get isNum => true; @override num get asNum => value; @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! NumJsonObject) return false; return value == other.value; } @override int get hashCode => value.hashCode; } /// A [JsonObject] holding a String. class StringJsonObject extends JsonObject { @override final String value; StringJsonObject(this.value) : super._(); @override bool get isString => true; @override String get asString => value; @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! StringJsonObject) return false; return value == other.value; } @override int get hashCode => value.hashCode; }