The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
persist_context.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2010 - 2025
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"
22
23config pack_scalar(const std::string &name, const t_string &val)
24{
25 config cfg;
26 cfg[name] = val;
27 return cfg;
28}
29
30static std::string get_persist_cfg_name(const std::string &name_space) {
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 cfg_ = io::read(*file_stream);
42 } catch (const config::error &err) {
43 LOG_PERSIST << err.message;
44 }
45 }
46 }
47}
48
54
55bool 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 }
156 }
157 return ret;
158}
159
160config 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].t_str());
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}
202bool 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
244void 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
251std::string persist_context::get_node() const
252{
253 return namespace_.namespace_;
254}
map_location prev
Class for writing a config out to a file in pieces.
void write(const config &cfg, bool strong_quotes=false)
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
void remove_attribute(std::string_view key)
Definition config.cpp:162
config & add_child(std::string_view key)
Definition config.cpp:436
void append(const config &cfg)
Append data from another config object to this one.
Definition config.cpp:188
config & child_or_add(std::string_view key)
Returns a reference to the first child with the given key.
Definition config.cpp:401
void clear_children(T... keys)
Definition config.hpp:601
bool has_attribute(std::string_view key) const
Definition config.cpp:157
void remove_child(std::string_view key, std::size_t index)
Definition config.cpp:623
std::size_t child_count(std::string_view key) const
Definition config.cpp:292
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
Definition config.cpp:312
bool empty() const
Definition config.cpp:823
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition config.cpp:362
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)
const config * cfg
Declarations for File-IO.
New lexcical_cast header.
Standard logging facilities (interface).
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
const std::string wml_extension
std::string get_wml_persist_dir()
bool delete_file(const std::string &filename)
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=std::ios_base::binary, bool create_directory=true)
std::unique_ptr< std::istream > scoped_istream
std::unique_ptr< std::ostream > scoped_ostream
filesystem::scoped_istream istream_file(const std::string &fname, bool treat_failure_as_error=true)
config read(std::istream &in, abstract_validator *validator)
Definition parser.cpp:610
config pack_scalar(const std::string &name, const t_string &val)
static std::string get_persist_cfg_name(const std::string &name_space)
config pack_scalar(const std::string &, const t_string &)
#define LOG_PERSIST
#define i