using UnityEngine;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System;
using System.ComponentModel;
using ES3Types;
using ES3Internal;
public abstract class ES3Reader : System.IDisposable
{
/// The settings used to create this reader.
public ES3Settings settings;
protected int serializationDepth = 0;
#region ES3Reader Abstract Methods
internal abstract int Read_int();
internal abstract float Read_float();
internal abstract bool Read_bool();
internal abstract char Read_char();
internal abstract decimal Read_decimal();
internal abstract double Read_double();
internal abstract long Read_long();
internal abstract ulong Read_ulong();
internal abstract byte Read_byte();
internal abstract sbyte Read_sbyte();
internal abstract short Read_short();
internal abstract ushort Read_ushort();
internal abstract uint Read_uint();
internal abstract string Read_string();
internal abstract byte[] Read_byteArray();
internal abstract long Read_ref();
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public abstract string ReadPropertyName();
protected abstract Type ReadKeyPrefix(bool ignore = false);
protected abstract void ReadKeySuffix();
internal abstract byte[] ReadElement(bool skip=false);
/// Disposes of the reader and it's underlying stream.
public abstract void Dispose();
// Seeks to the given key. Note that the stream position will not be reset.
internal virtual bool Goto(string key)
{
if (key == null)
throw new ArgumentNullException("Key cannot be NULL when loading data.");
string currentKey;
while ((currentKey = ReadPropertyName()) != key)
{
if (currentKey == null)
return false;
Skip();
}
return true;
}
internal virtual bool StartReadObject()
{
serializationDepth++;
return false;
}
internal virtual void EndReadObject()
{
serializationDepth--;
}
internal abstract bool StartReadDictionary();
internal abstract void EndReadDictionary();
internal abstract bool StartReadDictionaryKey();
internal abstract void EndReadDictionaryKey();
internal abstract void StartReadDictionaryValue();
internal abstract bool EndReadDictionaryValue();
internal abstract bool StartReadCollection();
internal abstract void EndReadCollection();
internal abstract bool StartReadCollectionItem();
internal abstract bool EndReadCollectionItem();
#endregion
internal ES3Reader(ES3Settings settings, bool readHeaderAndFooter = true)
{
this.settings = settings;
}
// If this is not null, the next call to the Properties will return this name.
internal string overridePropertiesName = null;
/// Allows you to enumerate over each field name. This should only be used within an ES3Type file.
public virtual ES3ReaderPropertyEnumerator Properties
{
get
{
return new ES3ReaderPropertyEnumerator (this);
}
}
internal virtual ES3ReaderRawEnumerator RawEnumerator
{
get
{
return new ES3ReaderRawEnumerator (this);
}
}
/*
* Skips the current object in the stream.
* Stream position should be somewhere before the opening brace for the object.
* When this method successfully exits, it will be on the closing brace for the object.
*/
/// Skips the current object in the stream.
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public virtual void Skip()
{
ReadElement(true);
}
/// Reads a value of type T from the reader.
public virtual T Read()
{
return Read(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
}
/// Reads a value of type T from the reader into an existing object.
/// The object we want to read our value into.
public virtual void ReadInto(object obj)
{
ReadInto(obj, ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
}
/// Reads a property (i.e. a property name and value) from the reader, ignoring the property name and only returning the value.
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public T ReadProperty()
{
return ReadProperty(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public T ReadProperty(ES3Type type)
{
ReadPropertyName();
return Read(type);
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public long ReadRefProperty()
{
ReadPropertyName();
return Read_ref();
}
internal Type ReadType()
{
return ES3Reflection.GetType(Read(ES3Type_string.Instance));
}
/// Sets the value of a private property on an object.
/// The name of the property we want to set.
/// The value we want to set the property to.
/// The object containing the property we want to set.
/// The objectContainingProperty object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.
public object SetPrivateProperty(string name, object value, object objectContainingProperty)
{
var property = ES3Reflection.GetES3ReflectedProperty(objectContainingProperty.GetType(), name);
if (property.IsNull)
throw new MissingMemberException("A private property named " + name + " does not exist in the type " + objectContainingProperty.GetType());
property.SetValue(objectContainingProperty, value);
return objectContainingProperty;
}
/// Sets the value of a private field on an object.
/// The name of the field we want to set.
/// The value we want to set the field to.
/// The object containing the field we want to set.
/// The objectContainingField object. This is helpful if you're setting a private property on a struct or other immutable type and need to return the boxed value.
public object SetPrivateField(string name, object value, object objectContainingField)
{
var field = ES3Reflection.GetES3ReflectedMember(objectContainingField.GetType(), name);
if(field.IsNull)
throw new MissingMemberException("A private field named "+ name + " does not exist in the type "+objectContainingField.GetType());
field.SetValue(objectContainingField, value);
return objectContainingField;
}
#region Read(key) & Read(key, obj) methods
/// Reads a value from the reader with the given key.
/// The key which uniquely identifies our value.
public virtual T Read(string key)
{
if(!Goto(key))
throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\". Use Load(key, defaultValue) if you want to return a default value if the key does not exist.");
Type type = ReadTypeFromHeader();
T obj = Read(ES3TypeMgr.GetOrCreateES3Type(type));
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
return obj;
}
/// Reads a value from the reader with the given key, returning the default value if the key does not exist.
/// The key which uniquely identifies our value.
/// The value we want to return if this key does not exist in the reader.
public virtual T Read(string key, T defaultValue)
{
if(!Goto(key))
return defaultValue;
Type type = ReadTypeFromHeader();
T obj = Read(ES3TypeMgr.GetOrCreateES3Type(type));
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
return obj;
}
/// Reads a value from the reader with the given key into the provided object.
/// The key which uniquely identifies our value.
/// The object we want to load the value into.
public virtual void ReadInto(string key, T obj) where T : class
{
if(!Goto(key))
throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \""+settings.FullPath+"\"");
Type type = ReadTypeFromHeader();
ReadInto(obj, ES3TypeMgr.GetOrCreateES3Type(type));
//ReadKeySuffix(); //No need to read key suffix as we're returning. Doing so would throw an error at this point for BinaryReaders.
}
protected virtual void ReadObject(object obj, ES3Type type)
{
// Check for null.
if(StartReadObject())
return;
type.ReadInto(this, obj);
EndReadObject();
}
protected virtual T ReadObject(ES3Type type)
{
if(StartReadObject())
return default(T);
object obj = type.Read(this);
EndReadObject();
return (T)obj;
}
#endregion
#region Read(ES3Type) & Read(obj,ES3Type) methods
/*
* Parses the next JSON Object in the stream (i.e. must be between '{' and '}' chars).
* If the first character in the Stream is not a '{', it will throw an error.
* Will also read the terminating '}'.
* If we have reached the end of stream, it will return null.
*/
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public virtual T Read(ES3Type type)
{
if (type == null || type.isUnsupported)
throw new NotSupportedException("Type of " + type + " is not currently supported, and could not be loaded using reflection.");
else if (type.isPrimitive)
return (T)type.Read(this);
else if (type.isCollection)
return (T)((ES3CollectionType)type).Read(this);
else if (type.isDictionary)
return (T)((ES3DictionaryType)type).Read(this);
else
return ReadObject(type);
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public virtual void ReadInto(object obj, ES3Type type)
{
if(type == null || type.isUnsupported)
throw new NotSupportedException("Type of "+obj.GetType()+" is not currently supported, and could not be loaded using reflection.");
else if(type.isCollection)
((ES3CollectionType)type).ReadInto(this, obj);
else if(type.isDictionary)
((ES3DictionaryType)type).ReadInto(this, obj);
else
ReadObject(obj, type);
}
#endregion
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
internal Type ReadTypeFromHeader()
{
// Check whether we need to determine the type by reading the header.
if(typeof(T) == typeof(object))
return ReadKeyPrefix();
else if(settings.typeChecking)
{
Type type = ReadKeyPrefix();
if(type == null)
throw new TypeLoadException("Trying to load data of type " + typeof(T) + ", but the type of data contained in file no longer exists. This may be because the type has been removed from your project or renamed.");
else if (type != typeof(T))
throw new InvalidOperationException("Trying to load data of type " + typeof(T) + ", but data contained in file is type of " + type + ".");
return type;
}
else
{
ReadKeyPrefix(true);
return typeof(T);
}
}
/// Creates a new ES3Reader and loads the default file into it.
public static ES3Reader Create()
{
return Create(new ES3Settings());
}
/// Creates a new ES3Reader and loads a file in storage into it.
/// The relative or absolute path of the file we want to load into the reader.
public static ES3Reader Create(string filePath)
{
return Create(new ES3Settings(filePath));
}
/// Creates a new ES3Reader and loads a file in storage into it.
/// The relative or absolute path of the file we want to load into the reader.
/// The settings we want to use to override the default settings.
public static ES3Reader Create(string filePath, ES3Settings settings)
{
return Create(new ES3Settings(filePath, settings));
}
/// Creates a new ES3Reader and loads a file in storage into it.
/// The settings we want to use to override the default settings.
public static ES3Reader Create(ES3Settings settings)
{
Stream stream = ES3Stream.CreateStream(settings, ES3FileMode.Read);
if(stream == null)
return null;
// Get the baseWriter using the given Stream.
if (settings.format == ES3.Format.JSON)
return new ES3JSONReader(stream, settings);
return null;
}
/// Creates a new ES3Reader and loads the bytes provided into it.
public static ES3Reader Create(byte[] bytes)
{
return Create(bytes, new ES3Settings());
}
/// Creates a new ES3Reader and loads the bytes provided into it.
/// The settings we want to use to override the default settings.
public static ES3Reader Create(byte[] bytes, ES3Settings settings)
{
Stream stream = ES3Stream.CreateStream(new MemoryStream(bytes), settings, ES3FileMode.Read);
if(stream == null)
return null;
// Get the baseWriter using the given Stream.
if(settings.format == ES3.Format.JSON)
return new ES3JSONReader(stream, settings);
return null;
}
internal static ES3Reader Create(Stream stream, ES3Settings settings)
{
stream = ES3Stream.CreateStream(stream, settings, ES3FileMode.Read);
// Get the baseWriter using the given Stream.
if(settings.format == ES3.Format.JSON)
return new ES3JSONReader(stream, settings);
return null;
}
internal static ES3Reader Create(Stream stream, ES3Settings settings, bool readHeaderAndFooter)
{
// Get the baseWriter using the given Stream.
if(settings.format == ES3.Format.JSON)
return new ES3JSONReader(stream, settings, readHeaderAndFooter);
return null;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public class ES3ReaderPropertyEnumerator
{
public ES3Reader reader;
public ES3ReaderPropertyEnumerator(ES3Reader reader)
{
this.reader = reader;
}
public IEnumerator GetEnumerator()
{
string propertyName;
while(true)
{
// Allows us to repeat a property name or insert one of our own.
if(reader.overridePropertiesName != null)
{
string tempName = reader.overridePropertiesName;
reader.overridePropertiesName = null;
yield return tempName;
}
else
{
if((propertyName = reader.ReadPropertyName()) == null)
yield break;
yield return propertyName;
}
}
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public class ES3ReaderRawEnumerator
{
public ES3Reader reader;
public ES3ReaderRawEnumerator(ES3Reader reader)
{
this.reader = reader;
}
public IEnumerator GetEnumerator()
{
while(true)
{
string key = reader.ReadPropertyName();
if(key == null)
yield break;
Type type = reader.ReadTypeFromHeader