The Battle for Wesnoth  1.19.5+dev
persist_context.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2024
3  by Jody Northup
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "filesystem.hpp"
17 #include "lexical_cast.hpp"
18 #include "log.hpp"
19 #include "persist_context.hpp"
21 #include "serialization/parser.hpp"
22 
23 config pack_scalar(const std::string &name, const t_string &val)
24 {
25  config cfg;
26  cfg[name] = val;
27  return cfg;
28 }
29 
30 static std::string get_persist_cfg_name(const std::string &name_space) {
31  return (filesystem::get_dir(filesystem::get_user_data_dir() + "/persist/") + name_space + filesystem::wml_extension);
32 }
33 
35 {
36  std::string cfg_name = get_persist_cfg_name(namespace_.root_);
37  if (filesystem::file_exists(cfg_name) && !filesystem::is_directory(cfg_name)) {
39  if (!(file_stream->fail())) {
40  try {
41  read(cfg_,*file_stream);
42  } catch (const config::error &err) {
43  LOG_PERSIST << err.message;
44  }
45  }
46  }
47 }
48 
51 {
52  load();
53 }
54 
55 bool persist_file_context::clear_var(const std::string &global, bool immediate)
56 {
57  config bak;
58  config bactive;
59  if (immediate) {
60  bak = cfg_;
61  config *node = get_node(bak, namespace_);
62  if (node)
63  bactive = node->child_or_add("variables");
64  load();
65  }
66  config *active = get_node(cfg_, namespace_);
67  if (active == nullptr)
68  return false;
69 
70  bool ret = active->has_child("variables");
71  if (ret) {
72  config &cfg = active->mandatory_child("variables");
73  bool exists = cfg.has_attribute(global);
74  if (!exists) {
75  if (cfg.has_child(global)) {
76  exists = true;
77  std::string::const_iterator index_start = std::find(global.begin(),global.end(),'[');
78  if (index_start != global.end())
79  {
80  const std::string::const_iterator index_end = std::find(global.begin(),global.end(),']');
81  const std::string index_str(index_start+1,index_end);
82  std::size_t index = static_cast<std::size_t>(lexical_cast_default<int>(index_str));
83  cfg.remove_child(global,index);
84  if (immediate) bactive.remove_child(global,index);
85  } else {
86  cfg.clear_children(global);
87  if (immediate) bactive.clear_children(global);
88  }
89  }
90  }
91  if (exists) {
92  cfg.remove_attribute(global);
93  if (immediate) bactive.remove_attribute(global);
94  if (cfg.empty()) {
95  active->clear_children("variables");
96  active->remove_attribute("variables");
97  name_space working = namespace_;
98  while ((active->empty()) && (!working.lineage_.empty())) {
99  name_space prev = working.prev();
100  active = get_node(cfg_, prev);
101  active->clear_children(working.node_);
102  if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
103  active->clear_children("variables");
104  active->remove_attribute("variables");
105  }
106  working = prev;
107  }
108  }
109 
110  if (!in_transaction_)
111  ret = save_context();
112  else if (immediate) {
113  ret = save_context();
114  cfg_ = bak;
115  active = get_node(cfg_, namespace_);
116  if (active != nullptr) {
117  active->clear_children("variables");
118  active->remove_attribute("variables");
119  if (!bactive.empty())
120  active->add_child("variables",bactive);
121  }
122  } else {
123  ret = true;
124  }
125  } else {
126  if (immediate) {
127  cfg_ = bak;
128  active = get_node(cfg_, namespace_);
129  if (active != nullptr) {
130  active->clear_children("variables");
131  active->remove_attribute("variables");
132  if (!bactive.empty())
133  active->add_child("variables", bactive);
134  }
135  }
136  ret = exists;
137  }
138  }
139 
140  // TODO: figure out when this is the case and adjust the next loop
141  // condition accordingly. -- shadowm
142  assert(active);
143 
144  while (active && active->empty() && !namespace_.lineage_.empty()) {
146  active = get_node(cfg_, prev);
147  if (active == nullptr) {
148  break;
149  }
151  if (active->has_child("variables") && active->mandatory_child("variables").empty()) {
152  active->clear_children("variables");
153  active->remove_attribute("variables");
154  }
155  namespace_ = prev;
156  }
157  return ret;
158 }
159 
160 config persist_file_context::get_var(const std::string &global) const
161 {
162  config ret;
163  const config *active = get_node(cfg_, namespace_);
164  if (active && (active->has_child("variables"))) {
165  const config &cfg = active->mandatory_child("variables");
166  std::size_t arrsize = cfg.child_count(global);
167  if (arrsize > 0) {
168  for (std::size_t i = 0; i < arrsize; i++)
169  ret.add_child(global, cfg.mandatory_child(global,i));
170  } else {
171  ret = pack_scalar(global,cfg[global]);
172  }
173  } else {
174  ret = pack_scalar(global,"");
175  }
176  return ret;
177 }
179  bool success = false;
180 
181  std::string cfg_name = get_persist_cfg_name(namespace_.root_);
182  if (!cfg_name.empty()) {
183  if (cfg_.empty()) {
184  success = filesystem::delete_file(cfg_name);
185  } else {
187  if (!out->fail())
188  {
189  config_writer writer(*out,false);
190  try {
191  writer.write(cfg_);
192  success = true;
193  } catch(config::error &err) {
194  LOG_PERSIST << err.message;
195  success = false;
196  }
197  }
198  }
199  }
200  return success;
201 }
202 bool persist_file_context::set_var(const std::string &global,const config &val, bool immediate)
203 {
204  config bak;
205  config bactive;
206  if (immediate) {
207  bak = cfg_;
208  bactive = get_node(bak, namespace_, true)->child_or_add("variables");
209  load();
210  }
211 
212  config *active = get_node(cfg_, namespace_, true);
213  config &cfg = active->child_or_add("variables");
214  if (val.has_attribute(global)) {
215  if (val[global].empty()) {
216  clear_var(global,immediate);
217  } else {
218  cfg[global] = val[global];
219  if (immediate) bactive[global] = val[global];
220  }
221  } else {
222  cfg.clear_children(global);
223  cfg.append(val);
224  if (immediate) {
225  bactive.clear_children(global);
226  bactive.append(val);
227  }
228  }
229 // dirty_ = true;
230  if (!in_transaction_)
231  return save_context();
232  else if (immediate) {
233  bool ret = save_context();
234  cfg_ = bak;
235  active = get_node(cfg_, namespace_, true);
236  active->clear_children("variables");
237  active->remove_attribute("variables");
238  active->add_child("variables",bactive);
239  return ret;
240  } else
241  return true;
242 }
243 
244 void persist_context::set_node(const std::string &name) {
245  std::string newspace = namespace_.root_;
246  if (!name.empty())
247  newspace += "." + name;
248  namespace_ = name_space(newspace);
249 }
250 
251 std::string persist_context::get_node() const
252 {
253  return namespace_.namespace_;
254 }
map_location prev
Definition: astarsearch.cpp:64
Class for writing a config out to a file in pieces.
void write(const config &cfg)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:203
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:366
void remove_child(config_key_type key, std::size_t index)
Definition: config.cpp:643
std::size_t child_count(config_key_type key) const
Definition: config.cpp:296
void clear_children(T... keys)
Definition: config.hpp:616
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:316
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:405
void remove_attribute(config_key_type key)
Definition: config.cpp:162
bool empty() const
Definition: config.cpp:849
config & add_child(config_key_type key)
Definition: config.cpp:440
std::string get_node() const
void set_node(const std::string &)
config get_var(const std::string &) const
bool set_var(const std::string &, const config &, bool immediate=false)
persist_file_context(const std::string &name_space)
bool clear_var(const std::string &, bool immediate=false)
Declarations for File-IO.
std::size_t i
Definition: function.cpp:1028
New lexcical_cast header.
Standard logging facilities (interface).
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error)
const std::string wml_extension
Definition: filesystem.hpp:81
static bfs::path get_dir(const bfs::path &dirpath)
Definition: filesystem.cpp:336
std::string get_user_data_dir()
Definition: filesystem.cpp:827
bool delete_file(const std::string &filename)
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:325
bool is_directory(const std::string &fname)
Returns true if the given file is a directory.
filesystem::scoped_ostream ostream_file(const std::string &fname, std::ios_base::openmode mode, bool create_directory)
std::unique_ptr< std::istream > scoped_istream
Definition: filesystem.hpp:53
std::unique_ptr< std::ostream > scoped_ostream
Definition: filesystem.hpp:54
bool exists(const image::locator &i_locator)
Returns true if the given image actually exists, without loading it.
Definition: picture.cpp:818
logger & err()
Definition: log.cpp:307
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
config pack_scalar(const std::string &name, const t_string &val)
static std::string get_persist_cfg_name(const std::string &name_space)
#define LOG_PERSIST
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:622