SaveEvent.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. using Comal.Classes;
  2. using InABox.Core;
  3. using InABox.Scripting;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Linq;
  8. using System.Text;
  9. namespace PRS.Shared.Events;
  10. public class SaveEvent<T> : IEvent<SaveEventDataModel<T>>
  11. where T : Entity
  12. {
  13. public Type Entity => typeof(T);
  14. public IEventDataModelDefinition DataModelDefinition()
  15. {
  16. return new SaveEventDataModelDefinition<T>(this);
  17. }
  18. public Notification GenerateNotification(SaveEventDataModel<T> model)
  19. {
  20. var notification = new Notification();
  21. notification.Title = $"Updated {typeof(T).Name}";
  22. notification.Description = $"Updated {typeof(T).Name}";
  23. if(model.Entity.ID != Guid.Empty)
  24. {
  25. notification.EntityType = CoreUtils.EntityName(model.EntityType);
  26. notification.EntityID = model.Entity.ID;
  27. }
  28. return notification;
  29. }
  30. public void SerializeBinary(CoreBinaryWriter writer)
  31. {
  32. }
  33. public void DeserializeBinary(CoreBinaryReader reader)
  34. {
  35. }
  36. }
  37. public class SaveEventDataModelDefinition<T>(SaveEvent<T> ev) : IEventDataModelDefinition
  38. where T : Entity
  39. {
  40. private IEventVariable[]? variables;
  41. public SaveEvent<T> Event { get; set; } = ev;
  42. public IEnumerable<IEventVariable> GetVariables()
  43. {
  44. if(variables is null)
  45. {
  46. variables = DatabaseSchema.AllProperties(Event.Entity).Select(x => new StandardEventVariable(x.Name, x.PropertyType)).ToArray();
  47. variables.SortBy(x => x.Name);
  48. }
  49. return variables;
  50. }
  51. public IEventVariable? GetVariable(string name)
  52. {
  53. if(variables is null)
  54. {
  55. var prop = DatabaseSchema.Property(Event.Entity, name);
  56. if(prop is null)
  57. {
  58. return null;
  59. }
  60. else
  61. {
  62. return new StandardEventVariable(prop.Name, prop.PropertyType);
  63. }
  64. }
  65. else
  66. {
  67. return variables.FirstOrDefault(x => x.Name == name);
  68. }
  69. }
  70. }
  71. public class SaveEventDataModel<T>(T entity) : IEventDataModel, ITypedEventDataModel
  72. where T : Entity
  73. {
  74. public T Entity { get; set; } = entity;
  75. public Type EntityType => typeof(T);
  76. public bool TryGetVariable(string name, out object? value)
  77. {
  78. var prop = DatabaseSchema.Property(typeof(T), name);
  79. if(prop != null)
  80. {
  81. value = prop.Getter()(Entity);
  82. return true;
  83. }
  84. else
  85. {
  86. value = null;
  87. return false;
  88. }
  89. }
  90. }
  91. #region Triggers
  92. public class CreatedSaveEventTrigger<T> : ITrigger<SaveEvent<T>, SaveEventDataModel<T>>
  93. where T : Entity
  94. {
  95. public bool Check(SaveEventDataModel<T> dataModel)
  96. {
  97. return dataModel.Entity.HasOriginalValue(x => x.ID);
  98. }
  99. public void SerializeBinary(CoreBinaryWriter writer)
  100. {
  101. }
  102. public void DeserializeBinary(CoreBinaryReader reader)
  103. {
  104. }
  105. }
  106. public class PropertyChangedSaveEventTrigger<T> : ITrigger<SaveEvent<T>, SaveEventDataModel<T>>
  107. where T : Entity
  108. {
  109. public IProperty? TriggerProperty { get; set; }
  110. public object? OldValue { get; set; }
  111. public object? NewValue { get; set; }
  112. public bool Check(SaveEventDataModel<T> dataModel)
  113. {
  114. if(TriggerProperty is null)
  115. {
  116. return false;
  117. }
  118. if (!dataModel.Entity.HasOriginalValue(TriggerProperty.Name))
  119. {
  120. return false;
  121. }
  122. if(OldValue is not null && !object.Equals(dataModel.Entity.OriginalValueList[TriggerProperty.Name], OldValue))
  123. {
  124. return false;
  125. }
  126. if(NewValue is not null && !object.Equals(TriggerProperty.Getter()(dataModel.Entity), NewValue))
  127. {
  128. return false;
  129. }
  130. return true;
  131. }
  132. public void SerializeBinary(CoreBinaryWriter writer)
  133. {
  134. if(TriggerProperty is null)
  135. {
  136. writer.Write(false);
  137. }
  138. else
  139. {
  140. writer.Write(true);
  141. writer.Write(TriggerProperty.Name);
  142. }
  143. }
  144. public void DeserializeBinary(CoreBinaryReader reader)
  145. {
  146. if(reader.ReadBoolean())
  147. {
  148. TriggerProperty = DatabaseSchema.Property(typeof(T), reader.ReadString());
  149. }
  150. else
  151. {
  152. TriggerProperty = null;
  153. }
  154. }
  155. }
  156. public class ScriptSaveEventTrigger<T> : ITrigger<SaveEvent<T>, SaveEventDataModel<T>>
  157. where T : Entity
  158. {
  159. private ScriptDocument? _scriptDocument;
  160. private string? _script;
  161. public string? Script
  162. {
  163. get => _script;
  164. set
  165. {
  166. if(_script != value)
  167. {
  168. _script = value;
  169. _scriptDocument = null;
  170. }
  171. }
  172. }
  173. public string DefaultScript()
  174. {
  175. return @"
  176. using Comal.Classes;
  177. public class Module
  178. {
  179. public bool Check(SaveEventDataModel<" + typeof(T).Name + @"> model)
  180. {
  181. // Return true if model.Entity meets the requirements for this event trigger.
  182. return true;
  183. }
  184. }";
  185. }
  186. public bool Check(SaveEventDataModel<T> dataModel)
  187. {
  188. if (Script is null) return false;
  189. if(_scriptDocument is null)
  190. {
  191. _scriptDocument = new(Script);
  192. _scriptDocument.Compile();
  193. }
  194. return _scriptDocument.Execute(methodname: "Check", parameters: [dataModel]);
  195. }
  196. public void SerializeBinary(CoreBinaryWriter writer)
  197. {
  198. writer.Write(Script ?? "");
  199. }
  200. public void DeserializeBinary(CoreBinaryReader reader)
  201. {
  202. var script = reader.ReadString();
  203. if (script.IsNullOrWhiteSpace())
  204. {
  205. Script = null;
  206. }
  207. else
  208. {
  209. Script = script;
  210. }
  211. }
  212. }
  213. #endregion
  214. #region Actions
  215. public class ScriptSaveEventAction<T> : IEventAction<SaveEvent<T>>
  216. where T : Entity
  217. {
  218. private ScriptDocument? _scriptDocument;
  219. private string? _script;
  220. public string? Script
  221. {
  222. get => _script;
  223. set
  224. {
  225. if(_script != value)
  226. {
  227. _script = value;
  228. _scriptDocument = null;
  229. }
  230. }
  231. }
  232. public string DefaultScript()
  233. {
  234. return @"
  235. using Comal.Classes;
  236. public class Module
  237. {
  238. public object? Result { get; set; }
  239. public bool Execute(SaveEventDataModel<" + typeof(T).Name + @"> model)
  240. {
  241. // Do anything you want with model.Entity, and then save return-value to 'Result', or leave it as 'null' if no return value is needed.
  242. return true;
  243. }
  244. }";
  245. }
  246. public object? Execute(IEventDataModel dataModel)
  247. {
  248. if (Script is null) return null;
  249. if(_scriptDocument is null)
  250. {
  251. _scriptDocument = new(Script);
  252. _scriptDocument.SetValue("Result", null);
  253. _scriptDocument.Compile();
  254. }
  255. var model = dataModel.RootModel<SaveEventDataModel<T>>();
  256. if(_scriptDocument.Execute(methodname: "Execute", parameters: [model]))
  257. {
  258. return _scriptDocument.GetValue("Result");
  259. }
  260. else
  261. {
  262. return null;
  263. }
  264. }
  265. public IEnumerable<string> ReferencedVariables()
  266. {
  267. yield break;
  268. }
  269. public void SerializeBinary(CoreBinaryWriter writer)
  270. {
  271. writer.Write(Script ?? "");
  272. }
  273. public void DeserializeBinary(CoreBinaryReader reader)
  274. {
  275. var script = reader.ReadString();
  276. if (script.IsNullOrWhiteSpace())
  277. {
  278. Script = null;
  279. }
  280. else
  281. {
  282. Script = script;
  283. }
  284. }
  285. }
  286. public class CreateEntityAction<T> : IEventAction<SaveEvent<T>>
  287. where T : Entity
  288. {
  289. public Type? EntityType { get; set; }
  290. public List<PropertyInitializer> Initializers { get; set; } = new List<PropertyInitializer>();
  291. public object? Execute(IEventDataModel dataModel)
  292. {
  293. if(EntityType is null)
  294. {
  295. return null;
  296. }
  297. var entity = (Activator.CreateInstance(EntityType) as Entity)!;
  298. foreach(var initializer in Initializers)
  299. {
  300. initializer.Execute(entity, dataModel);
  301. }
  302. return entity;
  303. }
  304. public IEnumerable<string> ReferencedVariables()
  305. {
  306. return Initializers.SelectMany(x => x.ReferencedVariables());
  307. }
  308. public void SerializeBinary(CoreBinaryWriter writer)
  309. {
  310. if(EntityType is null)
  311. {
  312. writer.Write(false);
  313. }
  314. else
  315. {
  316. writer.Write(true);
  317. writer.Write(EntityType.EntityName());
  318. }
  319. writer.Write(Initializers.Count);
  320. foreach(var init in Initializers)
  321. {
  322. init.SerializeBinary(writer);
  323. }
  324. }
  325. public void DeserializeBinary(CoreBinaryReader reader)
  326. {
  327. if(reader.ReadBoolean())
  328. {
  329. EntityType = CoreUtils.GetEntity(reader.ReadString());
  330. }
  331. else
  332. {
  333. EntityType = null;
  334. }
  335. var nInit = reader.ReadInt32();
  336. for(int i = 0; i < nInit; ++i)
  337. {
  338. var init = new PropertyInitializer(reader, EntityType!);
  339. Initializers.Add(init);
  340. }
  341. }
  342. }
  343. public class PropertyInitializer
  344. {
  345. public IProperty Property { get; set; }
  346. private CoreExpression? _valueExpression;
  347. private CoreExpression ValueExpression
  348. {
  349. get
  350. {
  351. _valueExpression ??= new CoreExpression(Value, Property.PropertyType);
  352. return _valueExpression;
  353. }
  354. }
  355. private string _value;
  356. public string Value
  357. {
  358. get => _value;
  359. [MemberNotNull(nameof(_value))]
  360. set
  361. {
  362. if(value != _value)
  363. {
  364. _value = value;
  365. _valueExpression = null;
  366. }
  367. }
  368. }
  369. /// <summary>
  370. /// <b>Only for use in serialisation.</b>
  371. /// </summary>
  372. public PropertyInitializer(CoreBinaryReader reader, Type entityType)
  373. {
  374. DeserializeBinary(reader, entityType);
  375. }
  376. public PropertyInitializer(IProperty property, string value)
  377. {
  378. Property = property;
  379. Value = value;
  380. }
  381. public void Execute(Entity entity, IEventDataModel dataModel)
  382. {
  383. Property.Setter()(entity, ValueExpression.Evaluate(dataModel));
  384. }
  385. public IEnumerable<string> ReferencedVariables()
  386. {
  387. return ValueExpression.ReferencedVariables;
  388. }
  389. public void SerializeBinary(CoreBinaryWriter writer)
  390. {
  391. writer.Write(Property.Name);
  392. writer.Write(Value);
  393. }
  394. [MemberNotNull(nameof(Property), nameof(_value))]
  395. public void DeserializeBinary(CoreBinaryReader reader, Type entityType)
  396. {
  397. Property = DatabaseSchema.PropertyStrict(entityType, reader.ReadString());
  398. Value = reader.ReadString();
  399. }
  400. }
  401. #endregion