ChildrenStorage.vala 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * ChildrenStorage - Low-level storage for structural children
  3. *
  4. * Handles the 'children:' prefix for storing container child names.
  5. *
  6. * Key format: children:<parent_path>
  7. * Value: Serialized array of child names
  8. *
  9. * @version 0.1
  10. * @since 0.1
  11. */
  12. namespace Implexus.Storage.LowLevel {
  13. /**
  14. * Low-level storage for structural children.
  15. *
  16. * This class provides type-safe operations for storing and retrieving
  17. * structural child names using the 'children:' key prefix.
  18. */
  19. public class ChildrenStorage : Object {
  20. /**
  21. * Key prefix for children entries.
  22. */
  23. private const string PREFIX = "children:";
  24. /**
  25. * The underlying Dbm storage.
  26. */
  27. private Dbm _dbm;
  28. /**
  29. * Creates a new ChildrenStorage with the given Dbm backend.
  30. *
  31. * @param dbm The Dbm backend to use for storage
  32. */
  33. public ChildrenStorage(Dbm dbm) {
  34. _dbm = dbm;
  35. }
  36. /**
  37. * Adds a child name to a parent's children set.
  38. *
  39. * @param parent The parent entity path
  40. * @param child_name The name of the child to add
  41. * @throws StorageError if the operation fails
  42. */
  43. public void add_child(Core.EntityPath parent, string child_name) throws StorageError {
  44. var children = load_children_set(parent);
  45. if (!children.contains(child_name)) {
  46. children.add(child_name);
  47. save_children_set(parent, children);
  48. }
  49. }
  50. /**
  51. * Removes a child name from a parent's children set.
  52. *
  53. * @param parent The parent entity path
  54. * @param child_name The name of the child to remove
  55. * @throws StorageError if the operation fails
  56. */
  57. public void remove_child(Core.EntityPath parent, string child_name) throws StorageError {
  58. var children = load_children_set(parent);
  59. if (children.contains(child_name)) {
  60. children.remove(child_name);
  61. save_children_set(parent, children);
  62. }
  63. }
  64. /**
  65. * Checks if a parent has a specific child.
  66. *
  67. * @param parent The parent entity path
  68. * @param child_name The name of the child to check
  69. * @return True if the child exists
  70. */
  71. public bool has_child(Core.EntityPath parent, string child_name) {
  72. var children = load_children_set(parent);
  73. return children.contains(child_name);
  74. }
  75. /**
  76. * Gets all child names for a parent.
  77. *
  78. * @param parent The parent entity path
  79. * @return An enumerable of child names
  80. */
  81. public Invercargill.Enumerable<string> get_children(Core.EntityPath parent) {
  82. var children = load_children_set(parent);
  83. return children.as_enumerable();
  84. }
  85. /**
  86. * Deletes all children for a parent.
  87. *
  88. * @param parent The parent entity path
  89. * @throws StorageError if the operation fails
  90. */
  91. public void delete(Core.EntityPath parent) throws StorageError {
  92. string key = PREFIX + parent.to_string();
  93. try {
  94. _dbm.delete(key);
  95. } catch (StorageError e) {
  96. // Key doesn't exist, that's fine
  97. }
  98. }
  99. /**
  100. * Loads the children set for a parent.
  101. *
  102. * @param parent The parent entity path
  103. * @return A vector of child names (empty if not found)
  104. */
  105. private Invercargill.DataStructures.Vector<string> load_children_set(Core.EntityPath parent) {
  106. string key = PREFIX + parent.to_string();
  107. var result = new Invercargill.DataStructures.Vector<string>();
  108. var data = _dbm.get(key);
  109. if (data == null) {
  110. return result;
  111. }
  112. var reader = new ElementReader((!) data);
  113. try {
  114. var element = reader.read_element();
  115. if (element.is_null()) {
  116. return result;
  117. }
  118. // The children set is stored as an array of strings
  119. var array = element.as<Invercargill.Enumerable<Invercargill.Element>>();
  120. foreach (var child_element in array) {
  121. if (!child_element.is_null()) {
  122. string child_name = child_element.as<string>();
  123. if (!result.contains(child_name)) {
  124. result.add(child_name);
  125. }
  126. }
  127. }
  128. } catch (Invercargill.ElementError e) {
  129. warning("Failed to read children set: %s", e.message);
  130. }
  131. return result;
  132. }
  133. /**
  134. * Saves the children set for a parent.
  135. *
  136. * @param parent The parent entity path
  137. * @param children The set of child names to save
  138. * @throws StorageError if the operation fails
  139. */
  140. private void save_children_set(Core.EntityPath parent, Invercargill.DataStructures.Vector<string> children) throws StorageError {
  141. string key = PREFIX + parent.to_string();
  142. uint count = children.count();
  143. if (count == 0) {
  144. try {
  145. _dbm.delete(key);
  146. } catch (StorageError e) {
  147. // Key doesn't exist, that's fine
  148. }
  149. return;
  150. }
  151. // Store as array of strings
  152. var array = new Invercargill.DataStructures.Vector<Invercargill.Element>();
  153. foreach (var child_name in children) {
  154. var element = new Invercargill.NativeElement<string>(child_name);
  155. array.add(element);
  156. }
  157. var writer = new ElementWriter();
  158. writer.write_element(new Invercargill.NativeElement<Invercargill.Enumerable<Invercargill.Element>>(array));
  159. _dbm.set(key, writer.to_binary_data());
  160. }
  161. }
  162. } // namespace Implexus.Storage.LowLevel