SchemaTypes.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Text;
  6. namespace SharpGLTF.SchemaReflection
  7. {
  8. /// <summary>
  9. /// Base class for all schema Types
  10. /// </summary>
  11. /// <remarks>
  12. /// Derived classes:
  13. /// - <see cref="StringType"/><br/>
  14. /// - <see cref="ObjectType"/><br/>
  15. /// - <see cref="BlittableType"/><br/>
  16. /// - <see cref="EnumType"/><br/>
  17. /// - <see cref="ArrayType"/><br/>
  18. /// - <see cref="DictionaryType"/><br/>
  19. /// - <see cref="ClassType"/><br/>
  20. /// - <see cref="ReferenceType"/><br/>
  21. /// </remarks>
  22. public abstract partial class SchemaType
  23. {
  24. #region constructor
  25. protected SchemaType(Context ctx) { _Owner = ctx; }
  26. #endregion
  27. #region data
  28. /// <summary>
  29. /// context where this type is stored
  30. /// </summary>
  31. private readonly Context _Owner;
  32. /// <summary>
  33. /// identifier used for serialization and deserialization
  34. /// </summary>
  35. public abstract string PersistentName { get; }
  36. #endregion
  37. #region properties
  38. public String Description { get; set; }
  39. public Context Owner => _Owner;
  40. #endregion
  41. }
  42. /// <summary>
  43. /// Represents a <see cref="String"/> type.
  44. /// </summary>
  45. [System.Diagnostics.DebuggerDisplay("StringType {PersistentName}")]
  46. public sealed class StringType : SchemaType
  47. {
  48. #region constructor
  49. internal StringType(Context ctx) : base(ctx) { }
  50. #endregion
  51. #region properties
  52. public override string PersistentName => typeof(String).Name;
  53. #endregion
  54. }
  55. /// <summary>
  56. /// Represents an undefined <see cref="Object"/> type.
  57. /// </summary>
  58. /// <remarks>
  59. /// undefined objects are serialized/deserialized as <see cref="System.Text.Json.Nodes.JsonNode"/>
  60. /// </remarks>
  61. [System.Diagnostics.DebuggerDisplay("ObjectType {PersistentName}")]
  62. public sealed class ObjectType : SchemaType
  63. {
  64. #region constructor
  65. internal ObjectType(Context ctx) : base(ctx) { }
  66. #endregion
  67. #region properties
  68. public override string PersistentName => typeof(System.Text.Json.Nodes.JsonNode).FullName;
  69. #endregion
  70. }
  71. /// <summary>
  72. /// Represents a "by value" type <see cref="Boolean"/>, <see cref="Int32"/>, <see cref="Single"/>, etc
  73. /// </summary>
  74. /// <remarks>
  75. /// This type can optionally be set as nullable.
  76. /// </remarks>
  77. [System.Diagnostics.DebuggerDisplay("BlittableType {PersistentName}")]
  78. public sealed class BlittableType : SchemaType
  79. {
  80. #region constructor
  81. internal BlittableType(Context ctx, TypeInfo t, bool isNullable) : base(ctx)
  82. {
  83. if (t == typeof(String).GetTypeInfo()) isNullable = false;
  84. _Type = t;
  85. _IsNullable = isNullable;
  86. }
  87. #endregion
  88. #region data
  89. // https://en.wikipedia.org/wiki/Blittable_types
  90. private readonly TypeInfo _Type;
  91. private readonly Boolean _IsNullable;
  92. #endregion
  93. #region properties
  94. public TypeInfo DataType => _Type;
  95. public bool IsNullable => _IsNullable;
  96. public override string PersistentName => _IsNullable ? $"{_Type.Name}?" : _Type.Name;
  97. #endregion
  98. }
  99. [System.Diagnostics.DebuggerDisplay("EnumType {PersistentName}")]
  100. public sealed class EnumType : SchemaType
  101. {
  102. #region constructor
  103. internal EnumType(Context ctx, string name, bool isNullable) : base(ctx)
  104. {
  105. _PersistentName = name;
  106. _IsNullable = isNullable;
  107. }
  108. #endregion
  109. #region data
  110. private readonly String _PersistentName;
  111. private readonly Boolean _IsNullable;
  112. private bool _UseIntegers;
  113. private readonly Dictionary<string, int> _Values = new Dictionary<string, int>();
  114. #endregion
  115. #region properties
  116. public bool IsNullable => _IsNullable;
  117. public override string PersistentName => _PersistentName;
  118. public bool UseIntegers { get => _UseIntegers; set => _UseIntegers = value; }
  119. public SchemaType ItemType => UseIntegers ? (SchemaType)Owner.UseBlittable(typeof(int).GetTypeInfo()) : Owner.UseString();
  120. public IEnumerable<KeyValuePair<string, int>> Values => _Values;
  121. #endregion
  122. #region API
  123. public void SetValue(string key, int val) { _Values[key] = val; }
  124. #endregion
  125. }
  126. [System.Diagnostics.DebuggerDisplay("ArrayType {PersistentName}")]
  127. public sealed class ArrayType : SchemaType
  128. {
  129. #region constructor
  130. internal ArrayType(Context ctx, SchemaType element) : base(ctx)
  131. {
  132. _ItemType = element;
  133. }
  134. #endregion
  135. #region data
  136. private readonly SchemaType _ItemType;
  137. public SchemaType ItemType => _ItemType;
  138. #endregion
  139. #region properties
  140. public override string PersistentName => $"{_ItemType.PersistentName}[]";
  141. #endregion
  142. }
  143. [System.Diagnostics.DebuggerDisplay("DictionaryType {PersistentName}")]
  144. public sealed class DictionaryType : SchemaType
  145. {
  146. #region lifecycle
  147. internal DictionaryType(Context ctx, SchemaType key,SchemaType val) : base(ctx)
  148. {
  149. _KeyType = key;
  150. _ValueType = val;
  151. }
  152. #endregion
  153. #region data
  154. private readonly SchemaType _KeyType;
  155. private readonly SchemaType _ValueType;
  156. #endregion
  157. #region properties
  158. public SchemaType KeyType => _KeyType;
  159. public SchemaType ValueType => _ValueType;
  160. public override string PersistentName => $"<{_KeyType.PersistentName},{_ValueType.PersistentName}>[]";
  161. #endregion
  162. }
  163. /// <summary>
  164. /// Represents a field property within a <see cref="ClassType"/>
  165. /// </summary>
  166. [System.Diagnostics.DebuggerDisplay("FieldInfo {_FieldType} {FieldType.PersistentName} {PersistentName}")]
  167. public sealed class FieldInfo
  168. {
  169. #region lifecycle
  170. internal FieldInfo(ClassType owner, string name)
  171. {
  172. _Owner = owner;
  173. _PersistentName = name;
  174. }
  175. #endregion
  176. #region data
  177. private readonly ClassType _Owner;
  178. private readonly String _PersistentName;
  179. private SchemaType _FieldType;
  180. private Object _ExclusiveMinimumValue;
  181. private Object _InclusiveMinimumValue;
  182. private Object _DefaultValue;
  183. private Object _InclusiveMaximumValue;
  184. private Object _ExclusiveMaximumValue;
  185. private int _MinItems;
  186. private int _MaxItems;
  187. #endregion
  188. #region properties
  189. public ClassType DeclaringClass => _Owner;
  190. public String Description { get; set; }
  191. public String PersistentName => _PersistentName;
  192. public SchemaType FieldType { get => _FieldType; set => _FieldType = value; }
  193. public Object ExclusiveMinimumValue { get => _ExclusiveMinimumValue; set => _ExclusiveMinimumValue = value; }
  194. public Object InclusiveMinimumValue { get => _InclusiveMinimumValue; set => _InclusiveMinimumValue = value; }
  195. public Object DefaultValue { get => _DefaultValue; set => _DefaultValue = value; }
  196. public Object InclusiveMaximumValue { get => _InclusiveMaximumValue; set => _InclusiveMaximumValue = value; }
  197. public Object ExclusiveMaximumValue { get => _ExclusiveMaximumValue; set => _ExclusiveMaximumValue = value; }
  198. public int MinItems { get => _MinItems; set => _MinItems = value; }
  199. public int MaxItems { get => _MaxItems; set => _MaxItems = value; }
  200. #endregion
  201. #region fluent api
  202. public FieldInfo SetDataType(SchemaType type) { _FieldType = type; return this; }
  203. public FieldInfo SetDataType(Type type, bool isNullable)
  204. {
  205. if (type == typeof(string)) { _FieldType = DeclaringClass.Owner.UseString(); return this; }
  206. _FieldType = DeclaringClass.Owner.UseBlittable(type.GetTypeInfo(), isNullable);
  207. return this;
  208. }
  209. public FieldInfo RemoveDefaultValue() { _DefaultValue = null; return this; }
  210. public FieldInfo SetDefaultValue(string defval) { _DefaultValue = defval; return this; }
  211. public FieldInfo SetLimits(Decimal? min, Decimal? max) { _InclusiveMinimumValue = min; _InclusiveMaximumValue = max; return this; }
  212. public FieldInfo SetItemsRange(int min, int max = int.MaxValue) { _MinItems = min; _MaxItems = max; return this; }
  213. #endregion
  214. #region comparer helper
  215. private sealed class _Comparer : IEqualityComparer<FieldInfo> , IComparer<FieldInfo>
  216. {
  217. public int Compare(FieldInfo x, FieldInfo y)
  218. {
  219. var xx = _Adjust(x._PersistentName);
  220. var yy = _Adjust(y._PersistentName);
  221. return string.CompareOrdinal(xx, yy);
  222. }
  223. private static string _Adjust(string name)
  224. {
  225. if (name.StartsWith("asset")) return "____" + name;
  226. if (name.StartsWith("extensions")) return "___" + name;
  227. return name;
  228. }
  229. public bool Equals(FieldInfo x, FieldInfo y) { return Compare(x,y) == 0; }
  230. public int GetHashCode(FieldInfo obj) { return obj._PersistentName.GetHashCode(StringComparison.Ordinal); }
  231. }
  232. private static readonly _Comparer _DefaultComparer = new _Comparer();
  233. public static IEqualityComparer<FieldInfo> EqualityComparer => _DefaultComparer;
  234. public static IComparer<FieldInfo> Comparer => _DefaultComparer;
  235. #endregion
  236. }
  237. [System.Diagnostics.DebuggerDisplay("ClassType {PersistentName} : {BaseClass.PersistentName}")]
  238. public sealed class ClassType : SchemaType
  239. {
  240. #region constructor
  241. internal ClassType(Context ctx, string name) : base(ctx)
  242. {
  243. _PersistentName = name;
  244. }
  245. #endregion
  246. #region data
  247. private readonly String _PersistentName;
  248. private readonly SortedSet<FieldInfo> _Fields = new SortedSet<FieldInfo>(FieldInfo.Comparer);
  249. private ClassType _BaseClass;
  250. public override string PersistentName => _PersistentName;
  251. public ClassType BaseClass { get => _BaseClass; set => _BaseClass = value; }
  252. public IEnumerable<FieldInfo> Fields => _Fields;
  253. /// <summary>
  254. /// True to prevent to codegen emitter to emit this class
  255. /// </summary>
  256. public bool IgnoredByEmitter { get; set; }
  257. #endregion
  258. #region API
  259. public FieldInfo GetField(string name)
  260. {
  261. return _Fields.First(item => item.PersistentName == name);
  262. }
  263. public FieldInfo UseField(string name)
  264. {
  265. var f = new FieldInfo(this, name);
  266. _Fields.Add(f);
  267. return GetField(name);
  268. }
  269. #endregion
  270. }
  271. /// <summary>
  272. /// not used
  273. /// </summary>
  274. public sealed class ReferenceType : SchemaType
  275. {
  276. #region constructor
  277. internal ReferenceType(Context ctx, SchemaType refType) : base(ctx)
  278. {
  279. _ReferencedType = refType;
  280. }
  281. #endregion
  282. #region data
  283. // In code it has the representation List<Node>();
  284. // In serialization, it has the representation List<int>();
  285. private readonly SchemaType _ReferencedType;
  286. #pragma warning disable CA1065 // Do not raise exceptions in unexpected locations
  287. public override string PersistentName => throw new NotImplementedException();
  288. #pragma warning restore CA1065 // Do not raise exceptions in unexpected locations
  289. #endregion
  290. }
  291. }