database.vala 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. /*
  2. * Copyright (c) 2012-2016 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/taylor001/crown/blob/master/LICENSE-GPLv2
  4. */
  5. using Gee;
  6. namespace Crown
  7. {
  8. public class Stack
  9. {
  10. private ArrayList<Value?> _data;
  11. public Stack()
  12. {
  13. _data = new ArrayList<Value?>();
  14. }
  15. public void push(Value? value)
  16. {
  17. _data.add(value);
  18. }
  19. public Value? pop()
  20. {
  21. return _data.remove_at(_data.size - 1);
  22. }
  23. public Value? peek()
  24. {
  25. return _data.last();
  26. }
  27. public int size()
  28. {
  29. return _data.size;
  30. }
  31. public void clear()
  32. {
  33. _data.clear();
  34. }
  35. }
  36. public class Database
  37. {
  38. private abstract class ChangeAction
  39. {
  40. }
  41. private class CreateAction : ChangeAction
  42. {
  43. public Guid _id;
  44. public CreateAction(Guid id)
  45. {
  46. _id = id;
  47. }
  48. }
  49. private class DestroyAction : ChangeAction
  50. {
  51. public Guid _id;
  52. public DestroyAction(Guid id)
  53. {
  54. _id = id;
  55. }
  56. }
  57. private class SetPropertyAction : ChangeAction
  58. {
  59. public Guid _id;
  60. public string _key;
  61. public Value? _value;
  62. public SetPropertyAction(Guid id, string key, Value? value)
  63. {
  64. _id = id;
  65. _key = key;
  66. _value = value;
  67. }
  68. }
  69. private class AddToSetAction : ChangeAction
  70. {
  71. public Guid _id;
  72. public string _key;
  73. public Guid _item_id;
  74. public AddToSetAction(Guid id, string key, Guid item_id)
  75. {
  76. _id = id;
  77. _key = key;
  78. _item_id = item_id;
  79. }
  80. }
  81. private class RemoveFromSetAction : ChangeAction
  82. {
  83. public Guid _id;
  84. public string _key;
  85. public Guid _item_id;
  86. public RemoveFromSetAction(Guid id, string key, Guid item_id)
  87. {
  88. _id = id;
  89. _key = key;
  90. _item_id = item_id;
  91. }
  92. }
  93. public delegate void ActionCallback(bool undo, int id, Value? data);
  94. private class RestorePoint : ChangeAction
  95. {
  96. public ActionCallback _undo_redo;
  97. public int _id;
  98. public Value? _data;
  99. public RestorePoint(ActionCallback undo_redo, int id, Value? data)
  100. {
  101. _undo_redo = undo_redo;
  102. _id = id;
  103. _data = data;
  104. }
  105. public void Redo()
  106. {
  107. if (_undo_redo != null)
  108. _undo_redo(false, _id, _data);
  109. }
  110. public void Undo()
  111. {
  112. if (_undo_redo != null)
  113. _undo_redo(true, _id, _data);
  114. }
  115. }
  116. // Data
  117. private HashMap<string, Value?> _data;
  118. private Stack _undo;
  119. private Stack _redo;
  120. private bool _changed;
  121. // Signals
  122. public signal void key_changed(Guid id, string key);
  123. public Database()
  124. {
  125. _data = new HashMap<string, Value?>();
  126. _undo = new Stack();
  127. _redo = new Stack();
  128. reset();
  129. }
  130. /// <summary>
  131. /// Resets database to clean state.
  132. /// </summary>
  133. public void reset()
  134. {
  135. _data.clear();
  136. _undo.clear();
  137. _redo.clear();
  138. _changed = false;
  139. // This is a special field which stores all objects
  140. _data.set("_objects", new HashMap<string, Value?>());
  141. }
  142. /// <summary>
  143. /// Returns whether the database has been changed since last call to Save().
  144. /// </summary>
  145. public bool changed()
  146. {
  147. return _changed;
  148. }
  149. /// <summary>
  150. /// Saves database to path.
  151. /// </summary>
  152. public void save(string path)
  153. {
  154. Hashtable json = encode();
  155. SJSON.save(json, path);
  156. _changed = false;
  157. }
  158. /// <summary>
  159. /// Loads database from path.
  160. /// </summary>
  161. public void load(string path)
  162. {
  163. Hashtable json = SJSON.load(path);
  164. decode(json);
  165. _changed = false;
  166. }
  167. private Hashtable encode()
  168. {
  169. return encode_object(_data);
  170. }
  171. private static bool is_valid_value(Value? value)
  172. {
  173. return value == null
  174. || value.holds(typeof(bool))
  175. || value.holds(typeof(double))
  176. || value.holds(typeof(string))
  177. || value.holds(typeof(Guid))
  178. || value.holds(typeof(Vector3))
  179. || value.holds(typeof(Quaternion))
  180. ;
  181. }
  182. private static bool is_valid_key(string key)
  183. {
  184. return key.length > 0
  185. && key != "_objects"
  186. && !key.has_prefix(".")
  187. && !key.has_suffix(".")
  188. ;
  189. }
  190. private static string value_to_string(Value? value)
  191. {
  192. if (value == null)
  193. return "null";
  194. if (value.holds(typeof(bool)))
  195. return ((bool)value).to_string();
  196. if (value.holds(typeof(double)))
  197. return ((double)value).to_string();
  198. if (value.holds(typeof(string)))
  199. return ((string)value).to_string();
  200. if (value.holds(typeof(Guid)))
  201. return ((Guid)value).to_string();
  202. if (value.holds(typeof(Vector3)))
  203. return ((Vector3)value).to_string();
  204. if (value.holds(typeof(Quaternion)))
  205. return ((Quaternion)value).to_string();
  206. if (value.holds(typeof(HashSet<Guid?>)))
  207. return "Set<Guid>";
  208. return "<invalid>";
  209. }
  210. private void decode(Hashtable json)
  211. {
  212. reset();
  213. decode_root_object(json);
  214. }
  215. private void decode_root_object(Hashtable json)
  216. {
  217. decode_object(GUID_ZERO, "", json);
  218. }
  219. private void decode_object(Guid id, string db_key, Hashtable json)
  220. {
  221. string old_db = db_key;
  222. string k = db_key;
  223. string[] keys = json.keys.to_array();
  224. foreach (string key in keys)
  225. {
  226. assert(key != "_objects");
  227. Value? val = json[key];
  228. k += k == "" ? key : ("." + key);
  229. if (val.holds(typeof(Hashtable)))
  230. {
  231. Hashtable ht = (Hashtable)val;
  232. if (is_set(ht))
  233. decode_set(id, key, ht);
  234. else
  235. decode_object(id, k, ht);
  236. }
  237. else
  238. {
  239. set_property_internal(id, k, decode_value(val));
  240. }
  241. k = old_db;
  242. }
  243. }
  244. private bool is_set(Hashtable json)
  245. {
  246. string[] keys = json.keys.to_array();
  247. foreach (string k in keys)
  248. {
  249. Guid guid;
  250. if (!Guid.try_parse(k, out guid))
  251. return false;
  252. }
  253. return true;
  254. }
  255. private void decode_set(Guid id, string key, Hashtable json)
  256. {
  257. create_empty_set(id, key);
  258. string[] keys = json.keys.to_array();
  259. foreach (string k in keys)
  260. {
  261. Guid item_id = Guid.parse(k);
  262. create_internal(item_id);
  263. decode_object(item_id, "", (Hashtable)json[k]);
  264. add_to_set_internal(id, key, item_id);
  265. }
  266. }
  267. private Value? decode_value(Value? value)
  268. {
  269. if (value.holds(typeof(ArrayList<Value?>)))
  270. {
  271. ArrayList<Value?> al = (ArrayList<Value?>)value;
  272. if (al.size == 3)
  273. return Vector3((double)al[0], (double)al[1], (double)al[2]);
  274. else if (al.size == 4)
  275. return Quaternion((double)al[0], (double)al[1], (double)al[2], (double)al[3]);
  276. else
  277. assert(false);
  278. }
  279. else if (value.holds(typeof(string)))
  280. {
  281. Guid id;
  282. if (Guid.try_parse((string)value, out id))
  283. return id;
  284. return value;
  285. }
  286. else if (value == null || value.holds(typeof(bool)) || value.holds(typeof(double)))
  287. {
  288. return value;
  289. }
  290. else
  291. {
  292. assert(false);
  293. }
  294. return null;
  295. }
  296. private Hashtable encode_object(HashMap<string, Value?> db)
  297. {
  298. Hashtable obj = new Hashtable();
  299. string[] keys = db.keys.to_array();
  300. foreach (string key in keys)
  301. {
  302. if (key == "_objects")
  303. continue;
  304. string[] foo = key.split(".");
  305. Hashtable x = obj;
  306. if (foo.length > 1)
  307. {
  308. for (int i = 0; i < foo.length - 1; ++i)
  309. {
  310. string f = foo[i];
  311. if (x.has_key(f))
  312. {
  313. x = (Hashtable)x[f];
  314. continue;
  315. }
  316. Hashtable y = new Hashtable();
  317. x.set(f, y);
  318. x = y;
  319. }
  320. }
  321. x.set(foo[foo.length-1], encode_value(db[key]));
  322. }
  323. return obj;
  324. }
  325. private Value? encode_value(Value? value)
  326. {
  327. assert(is_valid_value(value) || value.holds(typeof(HashSet<Guid?>)));
  328. if (value.holds(typeof(Vector3)))
  329. {
  330. Vector3 v = (Vector3)value;
  331. ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  332. arr.add(v.x);
  333. arr.add(v.y);
  334. arr.add(v.z);
  335. return arr;
  336. }
  337. else if (value.holds(typeof(Quaternion)))
  338. {
  339. Quaternion q = (Quaternion)value;
  340. ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
  341. arr.add(q.x);
  342. arr.add(q.y);
  343. arr.add(q.z);
  344. arr.add(q.w);
  345. return arr;
  346. }
  347. else if (value.holds(typeof(Guid)))
  348. {
  349. Guid id = (Guid)value;
  350. return "\"%s\"".printf(id.to_string());
  351. }
  352. else if (value.holds(typeof(HashSet<Guid?>)))
  353. {
  354. HashSet<Guid?> hs = (HashSet<Guid?>)value;
  355. Hashtable ht = new Hashtable();
  356. foreach (Guid id in hs)
  357. {
  358. HashMap<string, Value?> objs = (HashMap<string, Value?>)_data["_objects"];
  359. ht.set(id.to_string(), encode_object((HashMap<string, Value?>)objs[id.to_string()]));
  360. }
  361. return ht;
  362. }
  363. else
  364. {
  365. return value;
  366. }
  367. }
  368. private HashMap<string, Value?> get_data(Guid id)
  369. {
  370. assert(has_object(id));
  371. return (HashMap<string, Value?>)(id == GUID_ZERO ? _data : (_data["_objects"] as HashMap<string, Value?>)[id.to_string()]);
  372. }
  373. private void create_internal(Guid id)
  374. {
  375. assert(id != GUID_ZERO);
  376. #if CROWN_DEBUG
  377. stdout.printf("create %s\n", id.to_string());
  378. #endif // CROWN_DEBUG
  379. (_data["_objects"] as HashMap<string, Value?>).set(id.to_string(), new HashMap<string, Value?>());
  380. _changed = true;
  381. key_changed(id, "_objects");
  382. }
  383. private void destroy_internal(Guid id)
  384. {
  385. assert(id != GUID_ZERO);
  386. assert(has_object(id));
  387. #if CROWN_DEBUG
  388. stdout.printf("destroy %s\n", id.to_string());
  389. #endif // CROWN_DEBUG
  390. (_data["_objects"] as HashMap<string, Value?>).unset(id.to_string());
  391. _changed = true;
  392. key_changed(id, "_objects");
  393. }
  394. private void set_property_internal(Guid id, string key, Value? value)
  395. {
  396. assert(has_object(id));
  397. assert(is_valid_key(key));
  398. assert(is_valid_value(value));
  399. #if CROWN_DEBUG
  400. stdout.printf("set_property %s %s %s\n"
  401. , id.to_string()
  402. , key
  403. , (value == null) ? "null" : value_to_string(value)
  404. );
  405. #endif // CROWN_DEBUG
  406. HashMap<string, Value?> ob = get_data(id);
  407. ob[key] = value;
  408. _changed = true;
  409. key_changed(id, key);
  410. }
  411. private void create_empty_set(Guid id, string key)
  412. {
  413. assert(has_object(id));
  414. assert(is_valid_key(key));
  415. HashMap<string, Value?> ob = get_data(id);
  416. assert(!ob.has_key(key));
  417. ob[key] = new HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  418. }
  419. private void add_to_set_internal(Guid id, string key, Guid item_id)
  420. {
  421. assert(has_object(id));
  422. assert(is_valid_key(key));
  423. assert(item_id != GUID_ZERO);
  424. assert(has_object(item_id));
  425. #if CROWN_DEBUG
  426. stdout.printf("add_to_set %s %s %s\n"
  427. , id.to_string()
  428. , key
  429. , item_id.to_string()
  430. );
  431. #endif // CROWN_DEBUG
  432. HashMap<string, Value?> ob = get_data(id);
  433. if (!ob.has_key(key))
  434. {
  435. HashSet<Guid?> hs = new HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
  436. hs.add(item_id);
  437. ob[key] = hs;
  438. }
  439. else
  440. {
  441. (ob[key] as HashSet<Guid?>).add(item_id);
  442. }
  443. _changed = true;
  444. key_changed(id, key);
  445. }
  446. private void remove_from_set_internal(Guid id, string key, Guid item_id)
  447. {
  448. assert(has_object(id));
  449. assert(is_valid_key(key));
  450. assert(item_id != GUID_ZERO);
  451. #if CROWN_DEBUG
  452. stdout.printf("remove_from_set %s %s %s\n"
  453. , id.to_string()
  454. , key
  455. , item_id.to_string()
  456. );
  457. #endif // CROWN_DEBUG
  458. HashMap<string, Value?> ob = get_data(id);
  459. (ob[key] as HashSet<Guid?>).remove(item_id);
  460. _changed = true;
  461. key_changed(id, key);
  462. }
  463. public void create(Guid id)
  464. {
  465. assert(id != GUID_ZERO);
  466. assert(!has_object(id));
  467. _undo.push(new DestroyAction(id));
  468. _redo.clear();
  469. create_internal(id);
  470. }
  471. public void destroy(Guid id)
  472. {
  473. assert(id != GUID_ZERO);
  474. assert(has_object(id));
  475. HashMap<string, Value?> o = get_data(id);
  476. string[] keys = o.keys.to_array();
  477. foreach (string key in keys)
  478. {
  479. Value? value = o[key];
  480. if (value.holds(typeof(HashSet<Guid?>)))
  481. {
  482. HashSet<Guid?> hs = (HashSet<Guid?>)value;
  483. Guid?[] ids = hs.to_array();
  484. foreach (Guid item_id in ids)
  485. {
  486. remove_from_set(id, key, item_id);
  487. destroy(item_id);
  488. }
  489. }
  490. else
  491. {
  492. set_property(id, key, null);
  493. }
  494. }
  495. _undo.push(new CreateAction(id));
  496. _redo.clear();
  497. destroy_internal(id);
  498. }
  499. public void set_property(Guid id, string key, Value? value)
  500. {
  501. assert(has_object(id));
  502. assert(is_valid_key(key));
  503. assert(is_valid_value(value));
  504. HashMap<string, Value?> ob = get_data(id);
  505. _undo.push(new SetPropertyAction(id, key, ob.has_key(key) ? ob[key] : null));
  506. _redo.clear();
  507. set_property_internal(id, key, value);
  508. }
  509. public void add_to_set(Guid id, string key, Guid item_id)
  510. {
  511. assert(has_object(id));
  512. assert(is_valid_key(key));
  513. assert(item_id != GUID_ZERO);
  514. assert(has_object(item_id));
  515. _undo.push(new RemoveFromSetAction(id, key, item_id));
  516. _redo.clear();
  517. add_to_set_internal(id, key, item_id);
  518. }
  519. public void remove_from_set(Guid id, string key, Guid item_id)
  520. {
  521. assert(has_object(id));
  522. assert(is_valid_key(key));
  523. assert(item_id != GUID_ZERO);
  524. _undo.push(new AddToSetAction(id, key, item_id));
  525. _redo.clear();
  526. remove_from_set_internal(id, key, item_id);
  527. }
  528. public bool has_object(Guid id)
  529. {
  530. bool contains = (_data["_objects"] as HashMap<string, Value?>).has_key(id.to_string());
  531. return id == GUID_ZERO || contains;
  532. }
  533. public bool has_property(Guid id, string key)
  534. {
  535. assert(has_object(id));
  536. assert(is_valid_key(key));
  537. return get_data(id).has_key(key);
  538. }
  539. public Value? get_property(Guid id, string key)
  540. {
  541. assert(has_object(id));
  542. assert(is_valid_key(key));
  543. HashMap<string, Value?> ob = get_data(id);
  544. Value? value = (ob.has_key(key) ? ob[key] : null);
  545. #if CROWN_DEBUG
  546. stdout.printf("get_property %s %s %s\n"
  547. , id.to_string()
  548. , key
  549. , (value == null) ? "null" : value_to_string(value)
  550. );
  551. #endif // CROWN_DEBUG
  552. return value;
  553. }
  554. public HashMap<string, Value?> get_object(Guid id)
  555. {
  556. return (HashMap<string, Value?>)get_data(GUID_ZERO)[id.to_string()];
  557. }
  558. public string[] get_keys(Guid id)
  559. {
  560. HashMap<string, Value?> data = get_data(id);
  561. return data.keys.to_array();
  562. }
  563. public void add_restore_point(ActionCallback? undo_redo = null, int id = -1, Value? data = null)
  564. {
  565. #if CROWN_DEBUG
  566. stdout.printf("add_restore_point %d\n", id);
  567. #endif // CROWN_DEBUG
  568. _undo.push(new RestorePoint(undo_redo, id, data));
  569. }
  570. /// <summary>
  571. /// Duplicates the object specified by id and assign new_id to the duplicated object.
  572. /// </summary>
  573. public void duplicate(Guid id, Guid new_id)
  574. {
  575. assert(id != GUID_ZERO);
  576. assert(new_id != GUID_ZERO);
  577. assert(id != new_id);
  578. assert(has_object(id));
  579. create(new_id);
  580. HashMap<string, Value?> o = get_data(id);
  581. string[] keys = o.keys.to_array();
  582. foreach (string key in keys)
  583. {
  584. Value? val = o[key];
  585. if (val.holds(typeof(HashSet<Guid?>)))
  586. {
  587. HashSet<Guid?> hs = (HashSet<Guid?>)val;
  588. foreach (Guid j in hs)
  589. {
  590. Guid x = Guid.new_guid();
  591. duplicate(j, x);
  592. add_to_set(new_id, key, x);
  593. }
  594. }
  595. else
  596. {
  597. set_property(new_id, key, o[key]);
  598. }
  599. }
  600. }
  601. /// <summary>
  602. /// Copies the database to db under the given new_key.
  603. /// </summary>
  604. public void copy_to(Database db, string new_key)
  605. {
  606. assert(db != null);
  607. assert(is_valid_key(new_key));
  608. copy_deep(db, GUID_ZERO, new_key);
  609. }
  610. public void copy_deep(Database db, Guid id, string new_key)
  611. {
  612. HashMap<string, Value?> o = get_data(id);
  613. string[] keys = o.keys.to_array();
  614. foreach (string key in keys)
  615. {
  616. if (key == "_objects")
  617. continue;
  618. Value? value = o[key];
  619. if (value.holds(typeof(HashSet<Guid?>)))
  620. {
  621. HashSet<Guid?> hs = (HashSet<Guid?>)value;
  622. foreach (Guid j in hs)
  623. {
  624. db.create(j);
  625. copy_deep(db, j, "");
  626. db.add_to_set(id, new_key + (new_key == "" ? "" : ".") + key, j);
  627. }
  628. }
  629. else
  630. {
  631. db.set_property(id, new_key + (new_key == "" ? "" : ".") + key, o[key]);
  632. }
  633. }
  634. }
  635. public void undo_single_action()
  636. {
  637. RestorePoint begin_action = null;
  638. while (true)
  639. {
  640. if (_undo.size() == 0)
  641. {
  642. if (begin_action != null)
  643. {
  644. _redo.push(begin_action);
  645. begin_action.Undo();
  646. }
  647. return;
  648. }
  649. Value? action = _undo.peek();
  650. if (action.holds(typeof(RestorePoint)))
  651. {
  652. if (begin_action == null)
  653. {
  654. begin_action = (RestorePoint)action;
  655. _undo.pop();
  656. continue;
  657. }
  658. else
  659. {
  660. _redo.push(begin_action);
  661. begin_action.Undo();
  662. return;
  663. }
  664. }
  665. else if (action.holds(typeof(CreateAction)))
  666. {
  667. CreateAction a = (action as CreateAction);
  668. _redo.push(new DestroyAction(a._id));
  669. create_internal(a._id);
  670. }
  671. else if (action.holds(typeof(DestroyAction)))
  672. {
  673. DestroyAction a = (action as DestroyAction);
  674. _redo.push(new CreateAction(a._id));
  675. destroy_internal(a._id);
  676. }
  677. else if (action.holds(typeof(SetPropertyAction)))
  678. {
  679. SetPropertyAction a = (action as SetPropertyAction);
  680. _redo.push(new SetPropertyAction(a._id, a._key, get_data(a._id).has_key(a._key) ? get_data(a._id)[a._key] : null));
  681. set_property_internal(a._id, a._key, a._value);
  682. }
  683. else if (action.holds(typeof(AddToSetAction)))
  684. {
  685. AddToSetAction a = (action as AddToSetAction);
  686. _redo.push(new RemoveFromSetAction(a._id, a._key, a._item_id));
  687. add_to_set_internal(a._id, a._key, a._item_id);
  688. }
  689. else if (action.holds(typeof(RemoveFromSetAction)))
  690. {
  691. RemoveFromSetAction a = (action as RemoveFromSetAction);
  692. _redo.push(new AddToSetAction(a._id, a._key, a._item_id));
  693. remove_from_set_internal(a._id, a._key, a._item_id);
  694. }
  695. _undo.pop();
  696. }
  697. }
  698. public void redo_single_action()
  699. {
  700. RestorePoint begin_action = null;
  701. while (true)
  702. {
  703. if (_redo.size() == 0)
  704. {
  705. if (begin_action != null)
  706. {
  707. _undo.push(begin_action);
  708. begin_action.Redo();
  709. }
  710. return;
  711. }
  712. Value? action = _redo.peek();
  713. if (action.holds(typeof(RestorePoint)))
  714. {
  715. if (begin_action == null)
  716. {
  717. begin_action = (RestorePoint)action;
  718. _redo.pop();
  719. continue;
  720. }
  721. else
  722. {
  723. _undo.push(begin_action);
  724. begin_action.Redo();
  725. return;
  726. }
  727. }
  728. else if (action.holds(typeof(CreateAction)))
  729. {
  730. CreateAction a = (action as CreateAction);
  731. _undo.push(new DestroyAction(a._id));
  732. create_internal(a._id);
  733. }
  734. else if (action.holds(typeof(DestroyAction)))
  735. {
  736. DestroyAction a = (action as DestroyAction);
  737. _undo.push(new CreateAction(a._id));
  738. destroy_internal(a._id);
  739. }
  740. else if (action.holds(typeof(SetPropertyAction)))
  741. {
  742. SetPropertyAction a = (action as SetPropertyAction);
  743. _undo.push(new SetPropertyAction(a._id, a._key, get_data(a._id).has_key(a._key) ? get_data(a._id)[a._key] : null));
  744. set_property_internal(a._id, a._key, a._value);
  745. }
  746. else if (action.holds(typeof(AddToSetAction)))
  747. {
  748. AddToSetAction a = (action as AddToSetAction);
  749. _undo.push(new RemoveFromSetAction(a._id, a._key, a._item_id));
  750. add_to_set_internal(a._id, a._key, a._item_id);
  751. }
  752. else if (action.holds(typeof(RemoveFromSetAction)))
  753. {
  754. RemoveFromSetAction a = (action as RemoveFromSetAction);
  755. _undo.push(new AddToSetAction(a._id, a._key, a._item_id));
  756. remove_from_set_internal(a._id, a._key, a._item_id);
  757. }
  758. _redo.pop();
  759. }
  760. }
  761. }
  762. }