EP 0.1.1 Symbolization
This commit is contained in:
566
Convention/[Runtime]/EasySave/Readers/EasySaveJSONReader.cs
Normal file
566
Convention/[Runtime]/EasySave/Readers/EasySaveJSONReader.cs
Normal file
@@ -0,0 +1,566 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System;
|
||||
using Convention.EasySave.Types;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Convention.EasySave.Internal
|
||||
{
|
||||
/*
|
||||
* Specific EasySaveReader for reading JSON data.
|
||||
*
|
||||
* Note: Leading & trailing whitespace is ignored whenever
|
||||
* reading characters which are part of the JSON syntax,
|
||||
* i.e. { } [ ] , " " :
|
||||
*/
|
||||
public class EasySaveJSONReader : EasySaveReader
|
||||
{
|
||||
private const char endOfStreamChar = (char)65535;
|
||||
|
||||
public StreamReader baseReader;
|
||||
|
||||
internal EasySaveJSONReader(Stream stream, EasySaveSettings settings, bool readHeaderAndFooter = true) : base(settings, readHeaderAndFooter)
|
||||
{
|
||||
this.baseReader = new StreamReader(stream);
|
||||
|
||||
// Read opening brace from file if we're loading straight from file.
|
||||
if(readHeaderAndFooter)
|
||||
{
|
||||
try
|
||||
{
|
||||
SkipOpeningBraceOfFile();
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.Dispose();
|
||||
throw new FormatException("Cannot load from file because the data in it is not JSON data, or the data is encrypted.\nIf the save data is encrypted, please ensure that encryption is enabled when you load, and that you are using the same password used to encrypt the data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Property/Key Methods
|
||||
|
||||
/*
|
||||
* Reads the name of a property, and must be positioned (with or without whitespace) either:
|
||||
* - Before the '"' of a property name.
|
||||
* - Before the ',' separating properties.
|
||||
* - Before the '}' or ']' terminating this list of properties.
|
||||
* Can be used in conjunction with Read(EasySaveType) to read a property.
|
||||
*/
|
||||
public override string ReadPropertyName()
|
||||
{
|
||||
char c = PeekCharIgnoreWhitespace();
|
||||
|
||||
// Check whether there are any properties left to read.
|
||||
if(IsTerminator(c))
|
||||
return null;
|
||||
else if(c == ',')
|
||||
ReadCharIgnoreWhitespace();
|
||||
else if(!IsQuotationMark(c))
|
||||
throw new FormatException("Expected ',' separating properties or '\"' before property name, found '"+c+"'.");
|
||||
|
||||
var propertyName = Read_string();
|
||||
if(propertyName == null)
|
||||
throw new FormatException("Stream isn't positioned before a property.");
|
||||
|
||||
// Skip the ':' seperating property and value.
|
||||
ReadCharIgnoreWhitespace(':');
|
||||
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the type data prefixed to this key.
|
||||
* If ignore is true, it will return null to save the computation of converting
|
||||
* the string to a Type.
|
||||
*/
|
||||
protected override Type ReadKeyPrefix(bool ignoreType=false)
|
||||
{
|
||||
StartReadObject();
|
||||
|
||||
Type dataType = null;
|
||||
|
||||
string propertyName = ReadPropertyName();
|
||||
if(propertyName == EasySaveType.typeFieldName)
|
||||
{
|
||||
string typeString = Read_string();
|
||||
dataType = ignoreType ? null : EasySaveReflection.GetType(typeString);
|
||||
propertyName = ReadPropertyName();
|
||||
}
|
||||
|
||||
if(propertyName != "value")
|
||||
throw new FormatException("This data is not Easy Save Key Value data. Expected property name \"value\", found \""+propertyName+"\".");
|
||||
|
||||
return dataType;
|
||||
}
|
||||
|
||||
protected override void ReadKeySuffix()
|
||||
{
|
||||
EndReadObject();
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadObject()
|
||||
{
|
||||
base.StartReadObject();
|
||||
return ReadNullOrCharIgnoreWhitespace('{');
|
||||
}
|
||||
|
||||
internal override void EndReadObject()
|
||||
{
|
||||
ReadCharIgnoreWhitespace('}');
|
||||
base.EndReadObject();
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadDictionary()
|
||||
{
|
||||
return StartReadObject();
|
||||
}
|
||||
|
||||
internal override void EndReadDictionary(){}
|
||||
|
||||
internal override bool StartReadDictionaryKey()
|
||||
{
|
||||
// If this is an empty Dictionary, return false.
|
||||
if(PeekCharIgnoreWhitespace() == '}')
|
||||
{
|
||||
ReadCharIgnoreWhitespace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override void EndReadDictionaryKey()
|
||||
{
|
||||
ReadCharIgnoreWhitespace(':');
|
||||
}
|
||||
|
||||
internal override void StartReadDictionaryValue(){}
|
||||
|
||||
internal override bool EndReadDictionaryValue()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(c == '}')
|
||||
return true;
|
||||
// Else, we should expect a comma.
|
||||
else if(c != ',')
|
||||
throw new FormatException("Expected ',' seperating Dictionary items or '}' terminating Dictionary, found '"+c+"'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal override bool StartReadCollection()
|
||||
{
|
||||
return ReadNullOrCharIgnoreWhitespace('[');
|
||||
}
|
||||
|
||||
internal override void EndReadCollection(){}
|
||||
|
||||
internal override bool StartReadCollectionItem()
|
||||
{
|
||||
// If this is an empty collection, return false.
|
||||
if(PeekCharIgnoreWhitespace() == ']')
|
||||
{
|
||||
ReadCharIgnoreWhitespace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override bool EndReadCollectionItem()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
// If we find a ']', we reached the end of the array.
|
||||
if(c == ']')
|
||||
return true;
|
||||
// Else, we should expect a comma.
|
||||
else if(c != ',')
|
||||
throw new FormatException("Expected ',' seperating collection items or ']' terminating collection, found '"+c+"'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Seeking Methods
|
||||
|
||||
/*
|
||||
* Reads a string value into a StreamWriter.
|
||||
* Reader should be positioned after the opening quotation mark.
|
||||
* Will also read the closing quotation mark.
|
||||
* If the 'skip' parameter is true, data will not be written into a StreamWriter and will return null.
|
||||
*/
|
||||
private void ReadString(StreamWriter writer, bool skip=false)
|
||||
{
|
||||
bool endOfString = false;
|
||||
// Read to end of string, or throw error if we reach end of stream.
|
||||
while(!endOfString)
|
||||
{
|
||||
char c = ReadOrSkipChar(writer, skip);
|
||||
switch(c)
|
||||
{
|
||||
case endOfStreamChar:
|
||||
throw new FormatException("String without closing quotation mark detected.");
|
||||
case '\\':
|
||||
ReadOrSkipChar(writer, skip);
|
||||
break;
|
||||
default:
|
||||
if(IsQuotationMark(c))
|
||||
endOfString = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads 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.
|
||||
* If the 'skip' parameter is true, data will not be written into a StreamWriter and will return null.
|
||||
*/
|
||||
internal override byte[] ReadElement(bool skip=false)
|
||||
{
|
||||
// If 'skip' is enabled, don't create a stream or writer as we'll discard all bytes we read.
|
||||
StreamWriter writer = skip ? null : new StreamWriter(new MemoryStream(settings.bufferSize));
|
||||
|
||||
using(writer)
|
||||
{
|
||||
int nesting = 0;
|
||||
char c = (char)baseReader.Peek();
|
||||
|
||||
// Determine if we're skipping a primitive type.
|
||||
// First check if it's an opening object or array brace.
|
||||
if(!IsOpeningBrace(c))
|
||||
{
|
||||
// If we're skipping a string, use SkipString().
|
||||
if(c == '\"')
|
||||
{
|
||||
// Skip initial quotation mark as SkipString() requires this.
|
||||
ReadOrSkipChar(writer, skip);
|
||||
ReadString(writer, skip);
|
||||
}
|
||||
// Else we just need to read until we reach a closing brace.
|
||||
else
|
||||
// While we've not peeked a closing brace.
|
||||
while(!IsEndOfValue((char)baseReader.Peek()))
|
||||
ReadOrSkipChar(writer, skip);
|
||||
|
||||
if(skip)
|
||||
return null;
|
||||
writer.Flush();
|
||||
return ((MemoryStream)writer.BaseStream).ToArray();
|
||||
}
|
||||
|
||||
// Else, we're skipping a type surrounded by braces.
|
||||
// Iterate through every character, logging nesting.
|
||||
while(true)
|
||||
{
|
||||
c = ReadOrSkipChar(writer, skip);
|
||||
|
||||
if(c == endOfStreamChar) // Detect premature end of stream, which denotes missing closing brace.
|
||||
throw new FormatException("Missing closing brace detected, as end of stream was reached before finding it.");
|
||||
|
||||
// Handle quoted strings.
|
||||
// According to the RFC, only '\' and '"' must be escaped in strings.
|
||||
if(IsQuotationMark(c))
|
||||
{
|
||||
ReadString(writer, skip);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle braces and other characters.
|
||||
switch(c)
|
||||
{
|
||||
case '{': // Entered another level of nesting.
|
||||
case '[':
|
||||
nesting++;
|
||||
break;
|
||||
case '}': // Exited a level of nesting.
|
||||
case ']':
|
||||
nesting--;
|
||||
// If nesting < 1, we've come to the end of the object.
|
||||
if(nesting<1)
|
||||
{
|
||||
if(skip)
|
||||
return null;
|
||||
writer.Flush();
|
||||
return ((MemoryStream)writer.BaseStream).ToArray();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads the next char into a stream, or ignores it if 'skip' is true.
|
||||
*/
|
||||
private char ReadOrSkipChar(StreamWriter writer, bool skip)
|
||||
{
|
||||
char c = (char)baseReader.Read();
|
||||
if(!skip) writer.Write(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region JSON-specific methods.
|
||||
|
||||
/*
|
||||
* Reads a char from the stream and ignores leading and trailing whitespace.
|
||||
*/
|
||||
private char ReadCharIgnoreWhitespace(bool ignoreTrailingWhitespace=true)
|
||||
{
|
||||
char c;
|
||||
// Skip leading whitespace and read char.
|
||||
while(IsWhiteSpace(c = (char)baseReader.Read()))
|
||||
{}
|
||||
|
||||
// Skip trailing whitespace.
|
||||
if(ignoreTrailingWhitespace)
|
||||
while(IsWhiteSpace((char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a char, or the NULL value, from the stream and ignores leading and trailing whitespace.
|
||||
* Returns true if NULL was read.
|
||||
*/
|
||||
private bool ReadNullOrCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
|
||||
// Check for null
|
||||
if(c == 'n')
|
||||
{
|
||||
var chars = new char[3];
|
||||
baseReader.ReadBlock(chars, 0, 3);
|
||||
if((char)chars[0] == 'u' && (char)chars[1] == 'l' && (char)chars[2] == 'l')
|
||||
return true;
|
||||
}
|
||||
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\' or \"null\", found \'"+c+"\'.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a char from the stream and ignores leading and trailing whitespace.
|
||||
* Throws an error if the char isn't equal to the one specificed as a parameter, or if it's the end of stream.
|
||||
*/
|
||||
private char ReadCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace();
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\', found \'"+c+"\'.");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private bool ReadQuotationMarkOrNullIgnoreWhitespace()
|
||||
{
|
||||
char c = ReadCharIgnoreWhitespace(false); // Don't read trailing whitespace as this is the value.
|
||||
|
||||
if(c == 'n')
|
||||
{
|
||||
var chars = new char[3];
|
||||
baseReader.ReadBlock(chars, 0, 3);
|
||||
if((char)chars[0] == 'u' && (char)chars[1] == 'l' && (char)chars[2] == 'l')
|
||||
return true;
|
||||
}
|
||||
else if(!IsQuotationMark(c))
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached when expecting quotation mark.");
|
||||
else
|
||||
throw new FormatException("Expected quotation mark, found \'"+c+"\'.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Peeks the next char in the stream, ignoring leading whitespace, but not trailing whitespace.
|
||||
*/
|
||||
private char PeekCharIgnoreWhitespace(char expectedChar)
|
||||
{
|
||||
char c = PeekCharIgnoreWhitespace();
|
||||
if(c != expectedChar)
|
||||
{
|
||||
if(c == endOfStreamChar)
|
||||
throw new FormatException("End of stream reached while peeking, when expecting '"+expectedChar+"'.");
|
||||
else
|
||||
throw new FormatException("Expected \'"+expectedChar+"\', found \'"+c+"\'.");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Peeks the next char in the stream, ignoring leading whitespace, but not trailing whitespace.
|
||||
* Throws an error if the char isn't equal to the one specificed as a parameter.
|
||||
*/
|
||||
private char PeekCharIgnoreWhitespace()
|
||||
{
|
||||
char c;
|
||||
// Skip leading whitespace and read char.
|
||||
while(IsWhiteSpace(c = (char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
return c;
|
||||
}
|
||||
|
||||
// Skips all whitespace immediately after the current position.
|
||||
private void SkipWhiteSpace()
|
||||
{
|
||||
while(IsWhiteSpace((char)baseReader.Peek()))
|
||||
baseReader.Read();
|
||||
}
|
||||
|
||||
private void SkipOpeningBraceOfFile()
|
||||
{
|
||||
// Skip the whitespace and '{' at the beginning of the JSON file.
|
||||
char firstChar = ReadCharIgnoreWhitespace();
|
||||
if(firstChar != '{') // If first char isn't '{', it's not valid JSON.
|
||||
throw new FormatException("File is not valid JSON. Expected '{' at beginning of file, but found '"+firstChar+"'.");
|
||||
}
|
||||
|
||||
private static bool IsWhiteSpace(char c)
|
||||
{
|
||||
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
private static bool IsOpeningBrace(char c)
|
||||
{
|
||||
return (c == '{' || c == '[');
|
||||
}
|
||||
|
||||
private static bool IsEndOfValue(char c)
|
||||
{
|
||||
return (c == '}' || c == ' ' || c == '\t' || c == ']' || c == ',' || c== ':' || c == endOfStreamChar || c == '\n' || c == '\r');
|
||||
}
|
||||
|
||||
private static bool IsTerminator(char c)
|
||||
{
|
||||
return (c == '}' || c == ']');
|
||||
}
|
||||
|
||||
private static bool IsQuotationMark(char c)
|
||||
{
|
||||
return c == '\"' || c == '“' || c == '”';
|
||||
}
|
||||
|
||||
private static bool IsEndOfStream(char c)
|
||||
{
|
||||
return c == endOfStreamChar;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a value (i.e. non-string, non-object) from the stream as a string.
|
||||
* Used mostly in Read_[type]() methods.
|
||||
*/
|
||||
private string GetValueString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
while(!IsEndOfValue(PeekCharIgnoreWhitespace()))
|
||||
builder.Append((char)baseReader.Read());
|
||||
|
||||
// If it's an empty value, return null.
|
||||
if(builder.Length == 0)
|
||||
return null;
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Primitive Read() Methods.
|
||||
|
||||
internal override string Read_string()
|
||||
{
|
||||
if(ReadQuotationMarkOrNullIgnoreWhitespace())
|
||||
return null;
|
||||
char c;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
while(!IsQuotationMark((c = (char)baseReader.Read())))
|
||||
{
|
||||
// If escape mark is found, generate correct escaped character.
|
||||
if(c == '\\')
|
||||
{
|
||||
c = (char)baseReader.Read();
|
||||
if(IsEndOfStream(c))
|
||||
throw new FormatException("Reached end of stream while trying to read string literal.");
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case 'b':
|
||||
c = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
c = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
c = '\r';
|
||||
break;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
sb.Append(c);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal override long Read_ref()
|
||||
{
|
||||
if (IsQuotationMark(PeekCharIgnoreWhitespace()))
|
||||
return long.Parse(Read_string());
|
||||
return Read_long();
|
||||
}
|
||||
|
||||
internal override char Read_char() { return char.Parse( Read_string()); }
|
||||
internal override float Read_float() { return float.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override int Read_int() { return int.Parse( GetValueString()); }
|
||||
internal override bool Read_bool() { return bool.Parse( GetValueString()); }
|
||||
internal override decimal Read_decimal() { return decimal.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override double Read_double() { return double.Parse( GetValueString(), CultureInfo.InvariantCulture); }
|
||||
internal override long Read_long() { return long.Parse( GetValueString()); }
|
||||
internal override ulong Read_ulong() { return ulong.Parse( GetValueString()); }
|
||||
internal override uint Read_uint() { return uint.Parse( GetValueString()); }
|
||||
internal override byte Read_byte() { return (byte)int.Parse( GetValueString()); }
|
||||
internal override sbyte Read_sbyte() { return (sbyte)int.Parse( GetValueString()); }
|
||||
internal override short Read_short() { return (short)int.Parse( GetValueString()); }
|
||||
internal override ushort Read_ushort() { return (ushort)int.Parse( GetValueString()); }
|
||||
|
||||
internal override byte[] Read_byteArray(){ return System.Convert.FromBase64String(Read_string()); }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
baseReader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
457
Convention/[Runtime]/EasySave/Readers/EasySaveReader.cs
Normal file
457
Convention/[Runtime]/EasySave/Readers/EasySaveReader.cs
Normal file
@@ -0,0 +1,457 @@
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Convention.EasySave.Types;
|
||||
using Convention.EasySave.Internal;
|
||||
|
||||
namespace Convention.EasySave
|
||||
{
|
||||
public abstract class EasySaveReader : System.IDisposable
|
||||
{
|
||||
/// <summary>The settings used to create this reader.</summary>
|
||||
public EasySaveSettings settings;
|
||||
|
||||
protected int serializationDepth = 0;
|
||||
|
||||
#region EasySaveReader 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);
|
||||
|
||||
/// <summary>Disposes of the reader and it's underlying stream.</summary>
|
||||
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 EasySaveReader(EasySaveSettings 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;
|
||||
/// <summary>Allows you to enumerate over each field name. This should only be used within an EasySaveType file.</summary>
|
||||
public virtual EasySaveReaderPropertyEnumerator Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return new EasySaveReaderPropertyEnumerator(this);
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual EasySaveReaderRawEnumerator RawEnumerator
|
||||
{
|
||||
get
|
||||
{
|
||||
return new EasySaveReaderRawEnumerator(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.
|
||||
*/
|
||||
/// <summary>Skips the current object in the stream.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public virtual void Skip()
|
||||
{
|
||||
ReadElement(true);
|
||||
}
|
||||
|
||||
/// <summary>Reads a value of type T from the reader.</summary>
|
||||
public virtual T Read<T>()
|
||||
{
|
||||
return Read<T>(EasySaveTypeMgr.GetOrCreateEasySaveType(typeof(T)));
|
||||
}
|
||||
|
||||
/// <summary>Reads a value of type T from the reader into an existing object.</summary>
|
||||
/// <param name="obj">The object we want to read our value into.</param>
|
||||
public virtual void ReadInto<T>(object obj)
|
||||
{
|
||||
ReadInto<T>(obj, EasySaveTypeMgr.GetOrCreateEasySaveType(typeof(T)));
|
||||
}
|
||||
|
||||
/// <summary>Reads a property (i.e. a property name and value) from the reader, ignoring the property name and only returning the value.</summary>
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public T ReadProperty<T>()
|
||||
{
|
||||
return ReadProperty<T>(EasySaveTypeMgr.GetOrCreateEasySaveType(typeof(T)));
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public T ReadProperty<T>(EasySaveType type)
|
||||
{
|
||||
ReadPropertyName();
|
||||
return Read<T>(type);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public long ReadRefProperty()
|
||||
{
|
||||
ReadPropertyName();
|
||||
return Read_ref();
|
||||
}
|
||||
|
||||
internal Type ReadType()
|
||||
{
|
||||
return EasySaveReflection.GetType(Read<string>(EasySaveType_string.Instance));
|
||||
}
|
||||
|
||||
/// <summary>Sets the value of a private property on an object.</summary>
|
||||
/// <param name="name">The name of the property we want to set.</param>
|
||||
/// <param name="value">The value we want to set the property to.</param>
|
||||
/// <param name="objectContainingProperty">The object containing the property we want to set.</param>
|
||||
/// <returns>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.</returns>
|
||||
public object SetPrivateProperty(string name, object value, object objectContainingProperty)
|
||||
{
|
||||
var property = EasySaveReflection.GetEasySaveReflectedProperty(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;
|
||||
}
|
||||
|
||||
/// <summary>Sets the value of a private field on an object.</summary>
|
||||
/// <param name="name">The name of the field we want to set.</param>
|
||||
/// <param name="value">The value we want to set the field to.</param>
|
||||
/// <param name="objectContainingField">The object containing the field we want to set.</param>
|
||||
/// <returns>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.</returns>
|
||||
public object SetPrivateField(string name, object value, object objectContainingField)
|
||||
{
|
||||
var field = EasySaveReflection.GetEasySaveReflectedMember(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
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
public virtual T Read<T>(string key)
|
||||
{
|
||||
if (!Goto(key))
|
||||
throw new KeyNotFoundException("Key \"" + key + "\" was not found in file \"" + settings.FullPath + "\". Use Load<T>(key, defaultValue) if you want to return a default value if the key does not exist.");
|
||||
|
||||
Type type = ReadTypeFromHeader<T>();
|
||||
|
||||
T obj = Read<T>(EasySaveTypeMgr.GetOrCreateEasySaveType(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;
|
||||
}
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key, returning the default value if the key does not exist.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
/// <param name="defaultValue">The value we want to return if this key does not exist in the reader.</param>
|
||||
public virtual T Read<T>(string key, T defaultValue)
|
||||
{
|
||||
if (!Goto(key))
|
||||
return defaultValue;
|
||||
|
||||
Type type = ReadTypeFromHeader<T>();
|
||||
T obj = Read<T>(EasySaveTypeMgr.GetOrCreateEasySaveType(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;
|
||||
}
|
||||
|
||||
/// <summary>Reads a value from the reader with the given key into the provided object.</summary>
|
||||
/// <param name="key">The key which uniquely identifies our value.</param>
|
||||
/// <param name="obj">The object we want to load the value into.</param>
|
||||
public virtual void ReadInto<T>(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<T>();
|
||||
|
||||
ReadInto<T>(obj, EasySaveTypeMgr.GetOrCreateEasySaveType(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<T>(object obj, EasySaveType type)
|
||||
{
|
||||
// Check for null.
|
||||
if (StartReadObject())
|
||||
return;
|
||||
|
||||
type.ReadInto<T>(this, obj);
|
||||
|
||||
EndReadObject();
|
||||
}
|
||||
|
||||
protected virtual T ReadObject<T>(EasySaveType type)
|
||||
{
|
||||
if (StartReadObject())
|
||||
return default(T);
|
||||
|
||||
object obj = type.Read<T>(this);
|
||||
|
||||
EndReadObject();
|
||||
return (T)obj;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read(EasySaveType) & Read(obj,EasySaveType) 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<T>(EasySaveType 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<T>(this);
|
||||
else if (type.isCollection)
|
||||
return (T)((ECollectionType)type).Read(this);
|
||||
else if (type.isDictionary)
|
||||
return (T)((EasySaveDictionaryType)type).Read(this);
|
||||
else
|
||||
return ReadObject<T>(type);
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
public virtual void ReadInto<T>(object obj, EasySaveType 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)
|
||||
((ECollectionType)type).ReadInto(this, obj);
|
||||
else if (type.isDictionary)
|
||||
((EasySaveDictionaryType)type).ReadInto(this, obj);
|
||||
else
|
||||
ReadObject<T>(obj, type);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
|
||||
internal Type ReadTypeFromHeader<T>()
|
||||
{
|
||||
// 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 != 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads the default file into it.</summary>
|
||||
public static EasySaveReader Create()
|
||||
{
|
||||
return Create(new EasySaveSettings());
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads a file in storage into it.</summary>
|
||||
/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param>
|
||||
public static EasySaveReader Create(string filePath)
|
||||
{
|
||||
return Create(new EasySaveSettings(filePath));
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads a file in storage into it.</summary>
|
||||
/// <param name="filePath">The relative or absolute path of the file we want to load into the reader.</param>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static EasySaveReader Create(string filePath, EasySaveSettings settings)
|
||||
{
|
||||
return Create(new EasySaveSettings(filePath, settings));
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads a file in storage into it.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static EasySaveReader Create(EasySaveSettings settings)
|
||||
{
|
||||
Stream stream = EasySaveStream.CreateStream(settings, EasySaveFileMode.Read);
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if (settings.format == EasySave.Format.JSON)
|
||||
return new EasySaveJSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads the bytes provided into it.</summary>
|
||||
public static EasySaveReader Create(byte[] bytes)
|
||||
{
|
||||
return Create(bytes, new EasySaveSettings());
|
||||
}
|
||||
|
||||
/// <summary>Creates a new EasySaveReader and loads the bytes provided into it.</summary>
|
||||
/// <param name="settings">The settings we want to use to override the default settings.</param>
|
||||
public static EasySaveReader Create(byte[] bytes, EasySaveSettings settings)
|
||||
{
|
||||
Stream stream = EasySaveStream.CreateStream(new MemoryStream(bytes), settings, EasySaveFileMode.Read);
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if (settings.format == EasySave.Format.JSON)
|
||||
return new EasySaveJSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static EasySaveReader Create(Stream stream, EasySaveSettings settings)
|
||||
{
|
||||
stream = EasySaveStream.CreateStream(stream, settings, EasySaveFileMode.Read);
|
||||
|
||||
// Get the baseWriter using the given Stream.
|
||||
if (settings.format == EasySave.Format.JSON)
|
||||
return new EasySaveJSONReader(stream, settings);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static EasySaveReader Create(Stream stream, EasySaveSettings settings, bool readHeaderAndFooter)
|
||||
{
|
||||
// Get the baseWriter using the given Stream.
|
||||
if (settings.format == EasySave.Format.JSON)
|
||||
return new EasySaveJSONReader(stream, settings, readHeaderAndFooter);
|
||||
return null;
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public class EasySaveReaderPropertyEnumerator
|
||||
{
|
||||
public EasySaveReader reader;
|
||||
|
||||
public EasySaveReaderPropertyEnumerator(EasySaveReader 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 EasySaveReaderRawEnumerator
|
||||
{
|
||||
public EasySaveReader reader;
|
||||
|
||||
public EasySaveReaderRawEnumerator(EasySaveReader reader)
|
||||
{
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
string key = reader.ReadPropertyName();
|
||||
if (key == null)
|
||||
yield break;
|
||||
|
||||
Type type = reader.ReadTypeFromHeader<object>();
|
||||
|
||||
byte[] bytes = reader.ReadElement();
|
||||
|
||||
reader.ReadKeySuffix();
|
||||
|
||||
if (type != null)
|
||||
yield return new KeyValuePair<string, EasySaveData>(key, new EasySaveData(type, bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user