| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- /*
- * Copyright (c) 2012-2016 Daniele Bartolini and individual contributors.
- * License: https://github.com/taylor001/crown/blob/master/LICENSE-GPLv2
- */
- using Gee;
- namespace Crown
- {
- public class Stack
- {
- private ArrayList<Value?> _data;
- public Stack()
- {
- _data = new ArrayList<Value?>();
- }
- public void push(Value? value)
- {
- _data.add(value);
- }
- public Value? pop()
- {
- return _data.remove_at(_data.size - 1);
- }
- public Value? peek()
- {
- return _data.last();
- }
- public int size()
- {
- return _data.size;
- }
- public void clear()
- {
- _data.clear();
- }
- }
- public class Database
- {
- private abstract class ChangeAction
- {
- }
- private class CreateAction : ChangeAction
- {
- public Guid _id;
- public CreateAction(Guid id)
- {
- _id = id;
- }
- }
- private class DestroyAction : ChangeAction
- {
- public Guid _id;
- public DestroyAction(Guid id)
- {
- _id = id;
- }
- }
- private class SetPropertyAction : ChangeAction
- {
- public Guid _id;
- public string _key;
- public Value? _value;
- public SetPropertyAction(Guid id, string key, Value? value)
- {
- _id = id;
- _key = key;
- _value = value;
- }
- }
- private class AddToSetAction : ChangeAction
- {
- public Guid _id;
- public string _key;
- public Guid _item_id;
- public AddToSetAction(Guid id, string key, Guid item_id)
- {
- _id = id;
- _key = key;
- _item_id = item_id;
- }
- }
- private class RemoveFromSetAction : ChangeAction
- {
- public Guid _id;
- public string _key;
- public Guid _item_id;
- public RemoveFromSetAction(Guid id, string key, Guid item_id)
- {
- _id = id;
- _key = key;
- _item_id = item_id;
- }
- }
- public delegate void ActionCallback(bool undo, int id, Value? data);
- private class RestorePoint : ChangeAction
- {
- public ActionCallback _undo_redo;
- public int _id;
- public Value? _data;
- public RestorePoint(ActionCallback undo_redo, int id, Value? data)
- {
- _undo_redo = undo_redo;
- _id = id;
- _data = data;
- }
- public void Redo()
- {
- if (_undo_redo != null)
- _undo_redo(false, _id, _data);
- }
- public void Undo()
- {
- if (_undo_redo != null)
- _undo_redo(true, _id, _data);
- }
- }
- // Data
- private HashMap<string, Value?> _data;
- private Stack _undo;
- private Stack _redo;
- private bool _changed;
- // Signals
- public signal void key_changed(Guid id, string key);
- public Database()
- {
- _data = new HashMap<string, Value?>();
- _undo = new Stack();
- _redo = new Stack();
- reset();
- }
- /// <summary>
- /// Resets database to clean state.
- /// </summary>
- public void reset()
- {
- _data.clear();
- _undo.clear();
- _redo.clear();
- _changed = false;
- // This is a special field which stores all objects
- _data.set("_objects", new HashMap<string, Value?>());
- }
- /// <summary>
- /// Returns whether the database has been changed since last call to Save().
- /// </summary>
- public bool changed()
- {
- return _changed;
- }
- /// <summary>
- /// Saves database to path.
- /// </summary>
- public void save(string path)
- {
- Hashtable json = encode();
- SJSON.save(json, path);
- _changed = false;
- }
- /// <summary>
- /// Loads database from path.
- /// </summary>
- public void load(string path)
- {
- Hashtable json = SJSON.load(path);
- decode(json);
- _changed = false;
- }
- private Hashtable encode()
- {
- return encode_object(_data);
- }
- private static bool is_valid_value(Value? value)
- {
- return value == null
- || value.holds(typeof(bool))
- || value.holds(typeof(double))
- || value.holds(typeof(string))
- || value.holds(typeof(Guid))
- || value.holds(typeof(Vector3))
- || value.holds(typeof(Quaternion))
- ;
- }
- private static bool is_valid_key(string key)
- {
- return key.length > 0
- && key != "_objects"
- && !key.has_prefix(".")
- && !key.has_suffix(".")
- ;
- }
- private static string value_to_string(Value? value)
- {
- if (value == null)
- return "null";
- if (value.holds(typeof(bool)))
- return ((bool)value).to_string();
- if (value.holds(typeof(double)))
- return ((double)value).to_string();
- if (value.holds(typeof(string)))
- return ((string)value).to_string();
- if (value.holds(typeof(Guid)))
- return ((Guid)value).to_string();
- if (value.holds(typeof(Vector3)))
- return ((Vector3)value).to_string();
- if (value.holds(typeof(Quaternion)))
- return ((Quaternion)value).to_string();
- if (value.holds(typeof(HashSet<Guid?>)))
- return "Set<Guid>";
- return "<invalid>";
- }
- private void decode(Hashtable json)
- {
- reset();
- decode_root_object(json);
- }
- private void decode_root_object(Hashtable json)
- {
- decode_object(GUID_ZERO, "", json);
- }
- private void decode_object(Guid id, string db_key, Hashtable json)
- {
- string old_db = db_key;
- string k = db_key;
- string[] keys = json.keys.to_array();
- foreach (string key in keys)
- {
- assert(key != "_objects");
- Value? val = json[key];
- k += k == "" ? key : ("." + key);
- if (val.holds(typeof(Hashtable)))
- {
- Hashtable ht = (Hashtable)val;
- if (is_set(ht))
- decode_set(id, key, ht);
- else
- decode_object(id, k, ht);
- }
- else
- {
- set_property_internal(id, k, decode_value(val));
- }
- k = old_db;
- }
- }
- private bool is_set(Hashtable json)
- {
- string[] keys = json.keys.to_array();
- foreach (string k in keys)
- {
- Guid guid;
- if (!Guid.try_parse(k, out guid))
- return false;
- }
- return true;
- }
- private void decode_set(Guid id, string key, Hashtable json)
- {
- create_empty_set(id, key);
- string[] keys = json.keys.to_array();
- foreach (string k in keys)
- {
- Guid item_id = Guid.parse(k);
- create_internal(item_id);
- decode_object(item_id, "", (Hashtable)json[k]);
- add_to_set_internal(id, key, item_id);
- }
- }
- private Value? decode_value(Value? value)
- {
- if (value.holds(typeof(ArrayList<Value?>)))
- {
- ArrayList<Value?> al = (ArrayList<Value?>)value;
- if (al.size == 3)
- return Vector3((double)al[0], (double)al[1], (double)al[2]);
- else if (al.size == 4)
- return Quaternion((double)al[0], (double)al[1], (double)al[2], (double)al[3]);
- else
- assert(false);
- }
- else if (value.holds(typeof(string)))
- {
- Guid id;
- if (Guid.try_parse((string)value, out id))
- return id;
- return value;
- }
- else if (value == null || value.holds(typeof(bool)) || value.holds(typeof(double)))
- {
- return value;
- }
- else
- {
- assert(false);
- }
- return null;
- }
- private Hashtable encode_object(HashMap<string, Value?> db)
- {
- Hashtable obj = new Hashtable();
- string[] keys = db.keys.to_array();
- foreach (string key in keys)
- {
- if (key == "_objects")
- continue;
- string[] foo = key.split(".");
- Hashtable x = obj;
- if (foo.length > 1)
- {
- for (int i = 0; i < foo.length - 1; ++i)
- {
- string f = foo[i];
- if (x.has_key(f))
- {
- x = (Hashtable)x[f];
- continue;
- }
- Hashtable y = new Hashtable();
- x.set(f, y);
- x = y;
- }
- }
- x.set(foo[foo.length-1], encode_value(db[key]));
- }
- return obj;
- }
- private Value? encode_value(Value? value)
- {
- assert(is_valid_value(value) || value.holds(typeof(HashSet<Guid?>)));
- if (value.holds(typeof(Vector3)))
- {
- Vector3 v = (Vector3)value;
- ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
- arr.add(v.x);
- arr.add(v.y);
- arr.add(v.z);
- return arr;
- }
- else if (value.holds(typeof(Quaternion)))
- {
- Quaternion q = (Quaternion)value;
- ArrayList<Value?> arr = new Gee.ArrayList<Value?>();
- arr.add(q.x);
- arr.add(q.y);
- arr.add(q.z);
- arr.add(q.w);
- return arr;
- }
- else if (value.holds(typeof(Guid)))
- {
- Guid id = (Guid)value;
- return "\"%s\"".printf(id.to_string());
- }
- else if (value.holds(typeof(HashSet<Guid?>)))
- {
- HashSet<Guid?> hs = (HashSet<Guid?>)value;
- Hashtable ht = new Hashtable();
- foreach (Guid id in hs)
- {
- HashMap<string, Value?> objs = (HashMap<string, Value?>)_data["_objects"];
- ht.set(id.to_string(), encode_object((HashMap<string, Value?>)objs[id.to_string()]));
- }
- return ht;
- }
- else
- {
- return value;
- }
- }
- private HashMap<string, Value?> get_data(Guid id)
- {
- assert(has_object(id));
- return (HashMap<string, Value?>)(id == GUID_ZERO ? _data : (_data["_objects"] as HashMap<string, Value?>)[id.to_string()]);
- }
- private void create_internal(Guid id)
- {
- assert(id != GUID_ZERO);
- #if CROWN_DEBUG
- stdout.printf("create %s\n", id.to_string());
- #endif // CROWN_DEBUG
- (_data["_objects"] as HashMap<string, Value?>).set(id.to_string(), new HashMap<string, Value?>());
- _changed = true;
- key_changed(id, "_objects");
- }
- private void destroy_internal(Guid id)
- {
- assert(id != GUID_ZERO);
- assert(has_object(id));
- #if CROWN_DEBUG
- stdout.printf("destroy %s\n", id.to_string());
- #endif // CROWN_DEBUG
- (_data["_objects"] as HashMap<string, Value?>).unset(id.to_string());
- _changed = true;
- key_changed(id, "_objects");
- }
- private void set_property_internal(Guid id, string key, Value? value)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(is_valid_value(value));
- #if CROWN_DEBUG
- stdout.printf("set_property %s %s %s\n"
- , id.to_string()
- , key
- , (value == null) ? "null" : value_to_string(value)
- );
- #endif // CROWN_DEBUG
- HashMap<string, Value?> ob = get_data(id);
- ob[key] = value;
- _changed = true;
- key_changed(id, key);
- }
- private void create_empty_set(Guid id, string key)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- HashMap<string, Value?> ob = get_data(id);
- assert(!ob.has_key(key));
- ob[key] = new HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
- }
- private void add_to_set_internal(Guid id, string key, Guid item_id)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(item_id != GUID_ZERO);
- assert(has_object(item_id));
- #if CROWN_DEBUG
- stdout.printf("add_to_set %s %s %s\n"
- , id.to_string()
- , key
- , item_id.to_string()
- );
- #endif // CROWN_DEBUG
- HashMap<string, Value?> ob = get_data(id);
- if (!ob.has_key(key))
- {
- HashSet<Guid?> hs = new HashSet<Guid?>(Guid.hash_func, Guid.equal_func);
- hs.add(item_id);
- ob[key] = hs;
- }
- else
- {
- (ob[key] as HashSet<Guid?>).add(item_id);
- }
- _changed = true;
- key_changed(id, key);
- }
- private void remove_from_set_internal(Guid id, string key, Guid item_id)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(item_id != GUID_ZERO);
- #if CROWN_DEBUG
- stdout.printf("remove_from_set %s %s %s\n"
- , id.to_string()
- , key
- , item_id.to_string()
- );
- #endif // CROWN_DEBUG
- HashMap<string, Value?> ob = get_data(id);
- (ob[key] as HashSet<Guid?>).remove(item_id);
- _changed = true;
- key_changed(id, key);
- }
- public void create(Guid id)
- {
- assert(id != GUID_ZERO);
- assert(!has_object(id));
- _undo.push(new DestroyAction(id));
- _redo.clear();
- create_internal(id);
- }
- public void destroy(Guid id)
- {
- assert(id != GUID_ZERO);
- assert(has_object(id));
- HashMap<string, Value?> o = get_data(id);
- string[] keys = o.keys.to_array();
- foreach (string key in keys)
- {
- Value? value = o[key];
- if (value.holds(typeof(HashSet<Guid?>)))
- {
- HashSet<Guid?> hs = (HashSet<Guid?>)value;
- Guid?[] ids = hs.to_array();
- foreach (Guid item_id in ids)
- {
- remove_from_set(id, key, item_id);
- destroy(item_id);
- }
- }
- else
- {
- set_property(id, key, null);
- }
- }
- _undo.push(new CreateAction(id));
- _redo.clear();
- destroy_internal(id);
- }
- public void set_property(Guid id, string key, Value? value)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(is_valid_value(value));
- HashMap<string, Value?> ob = get_data(id);
- _undo.push(new SetPropertyAction(id, key, ob.has_key(key) ? ob[key] : null));
- _redo.clear();
- set_property_internal(id, key, value);
- }
- public void add_to_set(Guid id, string key, Guid item_id)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(item_id != GUID_ZERO);
- assert(has_object(item_id));
- _undo.push(new RemoveFromSetAction(id, key, item_id));
- _redo.clear();
- add_to_set_internal(id, key, item_id);
- }
- public void remove_from_set(Guid id, string key, Guid item_id)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- assert(item_id != GUID_ZERO);
- _undo.push(new AddToSetAction(id, key, item_id));
- _redo.clear();
- remove_from_set_internal(id, key, item_id);
- }
- public bool has_object(Guid id)
- {
- bool contains = (_data["_objects"] as HashMap<string, Value?>).has_key(id.to_string());
- return id == GUID_ZERO || contains;
- }
- public bool has_property(Guid id, string key)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- return get_data(id).has_key(key);
- }
- public Value? get_property(Guid id, string key)
- {
- assert(has_object(id));
- assert(is_valid_key(key));
- HashMap<string, Value?> ob = get_data(id);
- Value? value = (ob.has_key(key) ? ob[key] : null);
- #if CROWN_DEBUG
- stdout.printf("get_property %s %s %s\n"
- , id.to_string()
- , key
- , (value == null) ? "null" : value_to_string(value)
- );
- #endif // CROWN_DEBUG
- return value;
- }
- public HashMap<string, Value?> get_object(Guid id)
- {
- return (HashMap<string, Value?>)get_data(GUID_ZERO)[id.to_string()];
- }
- public string[] get_keys(Guid id)
- {
- HashMap<string, Value?> data = get_data(id);
- return data.keys.to_array();
- }
- public void add_restore_point(ActionCallback? undo_redo = null, int id = -1, Value? data = null)
- {
- #if CROWN_DEBUG
- stdout.printf("add_restore_point %d\n", id);
- #endif // CROWN_DEBUG
- _undo.push(new RestorePoint(undo_redo, id, data));
- }
- /// <summary>
- /// Duplicates the object specified by id and assign new_id to the duplicated object.
- /// </summary>
- public void duplicate(Guid id, Guid new_id)
- {
- assert(id != GUID_ZERO);
- assert(new_id != GUID_ZERO);
- assert(id != new_id);
- assert(has_object(id));
- create(new_id);
- HashMap<string, Value?> o = get_data(id);
- string[] keys = o.keys.to_array();
- foreach (string key in keys)
- {
- Value? val = o[key];
- if (val.holds(typeof(HashSet<Guid?>)))
- {
- HashSet<Guid?> hs = (HashSet<Guid?>)val;
- foreach (Guid j in hs)
- {
- Guid x = Guid.new_guid();
- duplicate(j, x);
- add_to_set(new_id, key, x);
- }
- }
- else
- {
- set_property(new_id, key, o[key]);
- }
- }
- }
- /// <summary>
- /// Copies the database to db under the given new_key.
- /// </summary>
- public void copy_to(Database db, string new_key)
- {
- assert(db != null);
- assert(is_valid_key(new_key));
- copy_deep(db, GUID_ZERO, new_key);
- }
- public void copy_deep(Database db, Guid id, string new_key)
- {
- HashMap<string, Value?> o = get_data(id);
- string[] keys = o.keys.to_array();
- foreach (string key in keys)
- {
- if (key == "_objects")
- continue;
- Value? value = o[key];
- if (value.holds(typeof(HashSet<Guid?>)))
- {
- HashSet<Guid?> hs = (HashSet<Guid?>)value;
- foreach (Guid j in hs)
- {
- db.create(j);
- copy_deep(db, j, "");
- db.add_to_set(id, new_key + (new_key == "" ? "" : ".") + key, j);
- }
- }
- else
- {
- db.set_property(id, new_key + (new_key == "" ? "" : ".") + key, o[key]);
- }
- }
- }
- public void undo_single_action()
- {
- RestorePoint begin_action = null;
- while (true)
- {
- if (_undo.size() == 0)
- {
- if (begin_action != null)
- {
- _redo.push(begin_action);
- begin_action.Undo();
- }
- return;
- }
- Value? action = _undo.peek();
- if (action.holds(typeof(RestorePoint)))
- {
- if (begin_action == null)
- {
- begin_action = (RestorePoint)action;
- _undo.pop();
- continue;
- }
- else
- {
- _redo.push(begin_action);
- begin_action.Undo();
- return;
- }
- }
- else if (action.holds(typeof(CreateAction)))
- {
- CreateAction a = (action as CreateAction);
- _redo.push(new DestroyAction(a._id));
- create_internal(a._id);
- }
- else if (action.holds(typeof(DestroyAction)))
- {
- DestroyAction a = (action as DestroyAction);
- _redo.push(new CreateAction(a._id));
- destroy_internal(a._id);
- }
- else if (action.holds(typeof(SetPropertyAction)))
- {
- SetPropertyAction a = (action as SetPropertyAction);
- _redo.push(new SetPropertyAction(a._id, a._key, get_data(a._id).has_key(a._key) ? get_data(a._id)[a._key] : null));
- set_property_internal(a._id, a._key, a._value);
- }
- else if (action.holds(typeof(AddToSetAction)))
- {
- AddToSetAction a = (action as AddToSetAction);
- _redo.push(new RemoveFromSetAction(a._id, a._key, a._item_id));
- add_to_set_internal(a._id, a._key, a._item_id);
- }
- else if (action.holds(typeof(RemoveFromSetAction)))
- {
- RemoveFromSetAction a = (action as RemoveFromSetAction);
- _redo.push(new AddToSetAction(a._id, a._key, a._item_id));
- remove_from_set_internal(a._id, a._key, a._item_id);
- }
- _undo.pop();
- }
- }
- public void redo_single_action()
- {
- RestorePoint begin_action = null;
- while (true)
- {
- if (_redo.size() == 0)
- {
- if (begin_action != null)
- {
- _undo.push(begin_action);
- begin_action.Redo();
- }
- return;
- }
- Value? action = _redo.peek();
- if (action.holds(typeof(RestorePoint)))
- {
- if (begin_action == null)
- {
- begin_action = (RestorePoint)action;
- _redo.pop();
- continue;
- }
- else
- {
- _undo.push(begin_action);
- begin_action.Redo();
- return;
- }
- }
- else if (action.holds(typeof(CreateAction)))
- {
- CreateAction a = (action as CreateAction);
- _undo.push(new DestroyAction(a._id));
- create_internal(a._id);
- }
- else if (action.holds(typeof(DestroyAction)))
- {
- DestroyAction a = (action as DestroyAction);
- _undo.push(new CreateAction(a._id));
- destroy_internal(a._id);
- }
- else if (action.holds(typeof(SetPropertyAction)))
- {
- SetPropertyAction a = (action as SetPropertyAction);
- _undo.push(new SetPropertyAction(a._id, a._key, get_data(a._id).has_key(a._key) ? get_data(a._id)[a._key] : null));
- set_property_internal(a._id, a._key, a._value);
- }
- else if (action.holds(typeof(AddToSetAction)))
- {
- AddToSetAction a = (action as AddToSetAction);
- _undo.push(new RemoveFromSetAction(a._id, a._key, a._item_id));
- add_to_set_internal(a._id, a._key, a._item_id);
- }
- else if (action.holds(typeof(RemoveFromSetAction)))
- {
- RemoveFromSetAction a = (action as RemoveFromSetAction);
- _undo.push(new AddToSetAction(a._id, a._key, a._item_id));
- remove_from_set_internal(a._id, a._key, a._item_id);
- }
- _redo.pop();
- }
- }
- }
- }
|