using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace FullSerializer {
///
/// The actual type that a JsonData instance can store.
///
public enum fsDataType {
Array,
Object,
Double,
Int64,
Boolean,
String,
Null
}
///
/// A union type that stores a serialized value. The stored type can be one of six different
/// types: null, boolean, double, Int64, string, Dictionary, or List.
///
public sealed class fsData {
///
/// The raw value that this serialized data stores. It can be one of six different types; a
/// boolean, a double, Int64, a string, a Dictionary, or a List.
///
private object _value;
#region Constructors
///
/// Creates a fsData instance that holds null.
///
public fsData() {
_value = null;
}
///
/// Creates a fsData instance that holds a boolean.
///
public fsData(bool boolean) {
_value = boolean;
}
///
/// Creates a fsData instance that holds a double.
///
public fsData(double f) {
_value = f;
}
///
/// Creates a new fsData instance that holds an integer.
///
public fsData(Int64 i) {
_value = i;
}
///
/// Creates a fsData instance that holds a string.
///
public fsData(string str) {
_value = str;
}
///
/// Creates a fsData instance that holds a dictionary of values.
///
public fsData(Dictionary dict) {
_value = dict;
}
///
/// Creates a fsData instance that holds a list of values.
///
public fsData(List list) {
_value = list;
}
///
/// Helper method to create a fsData instance that holds a dictionary.
///
public static fsData CreateDictionary() {
return new fsData(new Dictionary(
fsGlobalConfig.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase));
}
///
/// Helper method to create a fsData instance that holds a list.
///
public static fsData CreateList() {
return new fsData(new List());
}
///
/// Helper method to create a fsData instance that holds a list with the initial capacity.
///
public static fsData CreateList(int capacity) {
return new fsData(new List(capacity));
}
public readonly static fsData True = new fsData(true);
public readonly static fsData False = new fsData(false);
public readonly static fsData Null = new fsData();
#endregion
#region Internal Helper Methods
///
/// Transforms the internal fsData instance into a dictionary.
///
internal void BecomeDictionary() {
_value = new Dictionary();
}
///
/// Returns a shallow clone of this data instance.
///
internal fsData Clone() {
var clone = new fsData();
clone._value = _value;
return clone;
}
#endregion
#region Casting Predicates
public fsDataType Type {
get {
if (_value == null) return fsDataType.Null;
if (_value is double) return fsDataType.Double;
if (_value is Int64) return fsDataType.Int64;
if (_value is bool) return fsDataType.Boolean;
if (_value is string) return fsDataType.String;
if (_value is Dictionary) return fsDataType.Object;
if (_value is List) return fsDataType.Array;
throw new InvalidOperationException("unknown JSON data type");
}
}
///
/// Returns true if this fsData instance maps back to null.
///
public bool IsNull {
get {
return _value == null;
}
}
///
/// Returns true if this fsData instance maps back to a double.
///
public bool IsDouble {
get {
return _value is double;
}
}
///
/// Returns true if this fsData instance maps back to an Int64.
///
public bool IsInt64 {
get {
return _value is Int64;
}
}
///
/// Returns true if this fsData instance maps back to a boolean.
///
public bool IsBool {
get {
return _value is bool;
}
}
///
/// Returns true if this fsData instance maps back to a string.
///
public bool IsString {
get {
return _value is string;
}
}
///
/// Returns true if this fsData instance maps back to a Dictionary.
///
public bool IsDictionary {
get {
return _value is Dictionary;
}
}
///
/// Returns true if this fsData instance maps back to a List.
///
public bool IsList {
get {
return _value is List;
}
}
#endregion
#region Casts
///
/// Casts this fsData to a double. Throws an exception if it is not a double.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public double AsDouble {
get {
return Cast();
}
}
///
/// Casts this fsData to an Int64. Throws an exception if it is not an Int64.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public Int64 AsInt64 {
get {
return Cast();
}
}
///
/// Casts this fsData to a boolean. Throws an exception if it is not a boolean.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool AsBool {
get {
return Cast();
}
}
///
/// Casts this fsData to a string. Throws an exception if it is not a string.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string AsString {
get {
return Cast();
}
}
///
/// Casts this fsData to a Dictionary. Throws an exception if it is not a
/// Dictionary.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public Dictionary AsDictionary {
get {
return Cast>();
}
}
///
/// Casts this fsData to a List. Throws an exception if it is not a List.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List AsList {
get {
return Cast>();
}
}
///
/// Internal helper method to cast the underlying storage to the given type or throw a
/// pretty printed exception on failure.
///
private T Cast() {
if (_value is T) {
return (T)_value;
}
throw new InvalidCastException("Unable to cast <" + this + "> (with type = " +
_value.GetType() + ") to type " + typeof(T));
}
#endregion
#region ToString Implementation
public override string ToString() {
return fsJsonPrinter.CompressedJson(this);
}
#endregion
#region Equality Comparisons
///
/// Determines whether the specified object is equal to the current object.
///
public override bool Equals(object obj) {
return Equals(obj as fsData);
}
///
/// Determines whether the specified object is equal to the current object.
///
public bool Equals(fsData other) {
if (other == null || Type != other.Type) {
return false;
}
switch (Type) {
case fsDataType.Null:
return true;
case fsDataType.Double:
return AsDouble == other.AsDouble || Math.Abs(AsDouble - other.AsDouble) < double.Epsilon;
case fsDataType.Int64:
return AsInt64 == other.AsInt64;
case fsDataType.Boolean:
return AsBool == other.AsBool;
case fsDataType.String:
return AsString == other.AsString;
case fsDataType.Array:
var thisList = AsList;
var otherList = other.AsList;
if (thisList.Count != otherList.Count) return false;
for (int i = 0; i < thisList.Count; ++i) {
if (thisList[i].Equals(otherList[i]) == false) {
return false;
}
}
return true;
case fsDataType.Object:
var thisDict = AsDictionary;
var otherDict = other.AsDictionary;
if (thisDict.Count != otherDict.Count) return false;
foreach (string key in thisDict.Keys) {
if (otherDict.ContainsKey(key) == false) {
return false;
}
if (thisDict[key].Equals(otherDict[key]) == false) {
return false;
}
}
return true;
}
throw new Exception("Unknown data type");
}
///
/// Returns true iff a == b.
///
public static bool operator ==(fsData a, fsData b) {
// If both are null, or both are same instance, return true.
if (ReferenceEquals(a, b)) {
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null)) {
return false;
}
if (a.IsDouble && b.IsDouble) {
return Math.Abs(a.AsDouble - b.AsDouble) < double.Epsilon;
}
return a.Equals(b);
}
///
/// Returns true iff a != b.
///
public static bool operator !=(fsData a, fsData b) {
return !(a == b);
}
///
/// Returns a hash code for this instance.
///
/// A hash code for this instance, suitable for use in hashing algorithms and data
/// structures like a hash table.
public override int GetHashCode() {
return _value.GetHashCode();
}
#endregion
}
}