Newer
Older
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System;
using System.Linq;
using System.Collections;
using UnityEngine;
using Newtonsoft.Json.Linq;
using static CommunicationEvents;
// I would go for static virtual methods, but C#9 does not allow me...
// static methods cannot be overwritten -> virtual
public interface IJSONsavable<T> where T : IJSONsavable<T>, new()
{
Marco Zimmer
committed
// stand-in for non static methods
public static readonly IJSONsavable<T> Instance = new T();
public static readonly FieldInfo[] JsonSeperateFields =
typeof(T)
.GetFields(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static )
.Where((field)
=> field.GetCustomAttributes().Any((attribute)
=> attribute.GetType() == typeof(JSONManager.JsonSeparateAttribute))
&& field.FieldType.GetInterfaces().Any((inter)
=> inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
.ToArray();
Marco Zimmer
committed
// TODO: this?
public string name { get; set; }
public string path { get; set; }
protected static List<Directories>
hierarchie = new List<Directories> { Directories.misc };
#region OverridableMethods
Marco Zimmer
committed
public virtual string _IJGetName(string name) => name;
public virtual List<Directories> _IJGetHierarchie(List<Directories> hierarchie_base)
hierarchie_base ??= new List<Directories>();
return hierarchie_base.Concat(hierarchie).ToList();
Marco Zimmer
committed
public virtual bool _IJGetRawObject(out T payload, string path) => JSONManager.ReadFromJsonFile<T>(out payload, path);
public virtual T _IJPreProcess(T payload) => payload;
public virtual T _IJPostProcess(T payload) => payload;
#endregion OverridableMethods
Marco Zimmer
committed
Marco Zimmer
committed
public bool store(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
=> store(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store);
Marco Zimmer
committed
public static bool store(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
{
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
var new_name =
Instance._IJGetName(name);
string path = CreatePathToFile(out bool exists, new_name, "JSON", new_hierarchie, use_install_folder);
if (exists && !overwrite)
return false;
Marco Zimmer
committed
// store fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
if (deep_store
&& !store_children(hierarchie, name, payload, use_install_folder: false, overwrite, deep_store: true))
return false;
// store this
string path_o = payload.path;
payload.path = path;
var new_payload =
Instance._IJPreProcess(payload);
payload.path = path_o;
JSONManager.WriteToJsonFile(path, new_payload);
Marco Zimmer
committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public bool store_children(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
=> store_children(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store);
public static bool store_children(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
{
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
var new_name =
Instance._IJGetName(name);
for ((int max_i, bool success) = (0, true); max_i < JsonSeperateFields.Count(); max_i++)
{
var field = JsonSeperateFields[max_i];
dynamic save_me = field.GetValue(payload); // is S:IJSONsavable<S>
Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
Type[] store_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType, typeof(bool), typeof(bool), typeof(bool) };
object[] store_args = new object[] { new_hierarchie, new_name, save_me, use_install_folder, overwrite, deep_store };
var method = interface_type.GetMethod("store", store_args_type);
success &= (bool)method.Invoke(null, store_args);
// in case of no success: delete it again
if (!success)
{
delete_children(hierarchie, name, use_install_folder, JsonSeperateFields.Count() - max_i);
return false;
}
}
return true;
}
public static bool load_children(List<Directories> hierarchie, string name, ref T raw_payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
{
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
var new_name =
Instance._IJGetName(name);
bool success = true;
for (int max_i = 0; max_i < JsonSeperateFields.Count(); max_i++)
{
var field = JsonSeperateFields[max_i];
Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
Type[] load_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType.MakeByRefType(), typeof(bool), typeof(bool), typeof(bool) };
object[] load_args = new object[] { new_hierarchie, new_name, null, use_install_folder, deep_load, post_process };
var method = interface_type.GetMethod("load", BindingFlags.Public | BindingFlags.Static, null, load_args_type, null);
bool success_i = (bool)method.Invoke(null, load_args);
field.SetValue(raw_payload, success_i ? load_args[2] : Activator.CreateInstance(field.FieldType));
success &= success_i;
}
return success;
}
Marco Zimmer
committed
public static bool load(List<Directories> hierarchie, string name, out T payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
Marco Zimmer
committed
bool success = true;
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
Marco Zimmer
committed
var new_name =
Instance._IJGetName(name);
Marco Zimmer
committed
string path = CreatePathToFile(out bool loadable, new_name, "JSON", new_hierarchie, use_install_folder);
Marco Zimmer
committed
if (!Instance._IJGetRawObject(out T raw_payload, path))
return false;
raw_payload.name = new_name;
Marco Zimmer
committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
// load fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
if (deep_load
&& !load_children(hierarchie, name, ref raw_payload, false /*use_install_folder*/))
success = false;
payload = post_process
? Instance._IJPostProcess(raw_payload)
: raw_payload;
return success;
}
public static void delete_children(List<Directories> hierarchie, string name, bool use_install_folder = false, int skip_last_children = 0)
{
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
var new_name =
Instance._IJGetName(name);
for(int i = 0; i < JsonSeperateFields.Count() - skip_last_children; i++)
{
var field = JsonSeperateFields[i];
Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
Type[] delete_args_type = new Type[] { typeof(List<Directories>), typeof(string), typeof(bool) };
object[] delete_args = new object[] { new_hierarchie, new_name, use_install_folder };
var method = interface_type.GetMethod("delete", delete_args_type);
method.Invoke(null, delete_args);
}
public static bool delete(List<Directories> hierarchie, string name, bool use_install_folder = false)
var new_hierarchie =
Instance._IJGetHierarchie(hierarchie);
var new_name =
Instance._IJGetName(name);
string path = CreatePathToFile(out bool _, new_name, "JSON", new_hierarchie, use_install_folder);
Marco Zimmer
committed
if (!delete(path))
return false;
Marco Zimmer
committed
delete_children(hierarchie, name, use_install_folder);
return true;
Marco Zimmer
committed
// does not delete children!
private static bool delete(string path)
{
if (!File.Exists(path))
return false;
File.Delete(path);
return true;
}
Marco Zimmer
committed
// public bool delete() => delete(hierarchie, name);
#endregion MethodTemplates
public class MMTURICollection
{
public string Point = "http://mathhub.info/MitM/core/geometry?3DGeometry?point";
public string Tuple = "http://gl.mathhub.info/MMT/LFX/Sigma?Symbols?Tuple";
public string LineType = "http://mathhub.info/MitM/core/geometry?Geometry/Common?line_type";
public string LineOf = "http://mathhub.info/MitM/core/geometry?Geometry/Common?lineOf";
public string OnLine = "http://mathhub.info/MitM/core/geometry?Geometry/Common?onLine";
public string Ded = "http://mathhub.info/MitM/Foundation?Logic?ded";
public string Eq = "http://mathhub.info/MitM/Foundation?Logic?eq";
public string Metric = "http://mathhub.info/MitM/core/geometry?Geometry/Common?metric";
public string Angle = "http://mathhub.info/MitM/core/geometry?Geometry/Common?angle_between";
public string Sketch = "http://mathhub.info/MitM/Foundation?InformalProofs?proofsketch";
John Schihada
committed
public string RealLit = "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit";
public static class JSONManager
{
//could init the strings of MMTURIs with JSON or other settings file instead
public static MMTURICollection MMTURIs = new MMTURICollection();
public class URI
{
public string uri;
public URI(string uri)
{
this.uri = uri;
}
}
[JsonConverter(typeof(JsonSubtypes), "kind")]
public class MMTTerm
{
string kind;
}
public class OMA : MMTTerm
{
public MMTTerm applicant;
public List<MMTTerm> arguments;
public string kind = "OMA";
public OMA(MMTTerm applicant, List<MMTTerm> arguments)
{
this.applicant = applicant;
this.arguments = arguments;
}
}
public class OMS : MMTTerm
{
public string uri;
public string kind = "OMS";
public OMS(string uri)
{
this.uri = uri;
}
}
public class OMSTR : MMTTerm
{
[JsonProperty("float")]
public string s;
public string kind = "OMSTR";
public OMSTR(string s)
}
}
public class OMF : MMTTerm
{
[JsonProperty("float")]
public string kind = "OMF";
public OMF(float f)
{
}
}
public class MMTDeclaration
public string label;
public static MMTDeclaration FromJson(string json)
{
MMTDeclaration mmtDecl = JsonConvert.DeserializeObject<MMTDeclaration>(json);
if (mmtDecl.label == null)
mmtDecl.label = string.Empty;
return mmtDecl;
}
public static string ToJson(MMTDeclaration mmtDecl)
{
if (mmtDecl.label == null)
mmtDecl.label = string.Empty;
string json = JsonConvert.SerializeObject(mmtDecl);
return json;
}
}
/**
* MMTSymbolDeclaration: Class for facts without values, e.g. Points
public class MMTSymbolDeclaration : MMTDeclaration
public string kind = "general";
public MMTTerm tp;
public MMTTerm df;
/**
* Constructor used for sending new declarations to mmt
*/
public MMTSymbolDeclaration(string label, MMTTerm tp, MMTTerm df)
{
this.label = label;
this.tp = tp;
this.df = df;
}
/**
* MMTValueDeclaration: Class for facts with values, e.g. Distances or Angles
public class MMTValueDeclaration : MMTDeclaration
public string kind = "veq";
John Schihada
committed
public MMTTerm valueTp;
public MMTTerm value;
/**
* Constructor used for sending new declarations to mmt
*/
John Schihada
committed
public MMTValueDeclaration(string label, MMTTerm lhs, MMTTerm valueTp, MMTTerm value)
{
this.label = label;
this.lhs = lhs;
John Schihada
committed
this.valueTp = valueTp;
this.value = value;
Marco Zimmer
committed
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class JsonSeparateAttribute : Attribute
{
public JsonSeparateAttribute() { }
}
// TODO? /// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
/// <summary>
/// Writes the given object instance to a Json file, recursively to set depth, including all members.
/// <para>Object type must have a parameterless constructor.</para>
/// <para>Only All properties and variables will be written to the file. These can be any type though, even other non-abstract classes.</para>
/// </summary>
/// <param name="filePath">The file path to write the object instance to.</param>
/// <param name="objectToWrite">The object instance to write to the file.</param>
/// <param name="max_depth">The depth recursion will occur. Default = 0.</param>
Marco Zimmer
committed
public static bool WriteToJsonFile(string filePath, object objectToWrite, int max_depth = 0)
{
// This tells your serializer that multiple references are okay.
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
TextWriter writer = null;
try
{
Marco Zimmer
committed
string payload = JsonConvert.SerializeObject(objectToWrite, settings);
writer = new StreamWriter(filePath);
writer.Write(payload);
Marco Zimmer
committed
return true;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
finally
{
if (writer != null)
writer.Close();
}
}
/// <summary>
/// Reads an object instance from an Json file.
/// <para>Object type must have a parameterless constructor.</para>
/// </summary>
/// <typeparam name="T">The type of object to read from the file.</typeparam>
/// <param name="filePath">The file path to read the object instance from.</param>
/// <returns>Returns a new instance of the object read from the Json file.</returns>
Marco Zimmer
committed
public static bool ReadFromJsonFile<T>(out T payload, string filePath) where T : new()
Marco Zimmer
committed
payload = default(T);
TextReader reader = null;
Marco Zimmer
committed
try
{
reader = new StreamReader(filePath);
var fileContents = reader.ReadToEnd();
Marco Zimmer
committed
payload = JsonConvert.DeserializeObject<T>(fileContents);
return true;
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
finally
{
if (reader != null)
reader.Close();
}
}