1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file script_modify.h
12 *** \author Daniel Steuernol - steu@allacrost.org,
13 *** Tyler Olsen - roots@allacrost.org
14 *** \brief Header file for the ModifyScriptDescriptor class.
15 *** ***************************************************************************/
16
17 #ifndef __SCRIPT_MODIFY_HEADER__
18 #define __SCRIPT_MODIFY_HEADER__
19
20 #include "utils.h"
21 #include "defs.h"
22
23 #include "script.h"
24 #include "script_read.h"
25
26 namespace hoa_script {
27
28 /** ****************************************************************************
29 *** \brief Represents a Lua file opened with read, execute, and modify permissions.
30 ***
31 *** This class features all the functionality found in the ReadScriptDescriptor
32 *** class, plus the additional ability to modify existing Lua data and save it
33 *** back to the file. An object of this class should only be created if the user
34 *** intends to modify the Lua data in the file at some point (i.e. don't use
35 *** this class over the ReadScriptDescriptor simply because it has more
36 *** functionality available).
37 ***
38 *** In order to permanently (and irreversibly) change Lua data in the file, the
39 *** user must call the CommitChanges function after making one or several
40 *** ModifyData calls.
41 ***
42 *** \note This class and is features are still highly experimental and incomplete.
43 ***
44 *** \todo Add ability to modify tables and their data
45 *** ***************************************************************************/
46 class ModifyScriptDescriptor : public ReadScriptDescriptor {
47 friend class ScriptEngine;
48 public:
ModifyScriptDescriptor()49 ModifyScriptDescriptor()
50 {}
51
52 ~ModifyScriptDescriptor();
53
54 /** \name File Access Functions
55 *** \note These are derived from ScriptDescriptor, refer to the comments for these
56 *** methods in the header file for that class.
57 **/
58 //@{
59 bool OpenFile(const std::string& file_name);
60 bool OpenFile();
61 void CloseFile();
62 //@}
63
64 /** \name Data Modifier Functions
65 *** \brief These functions allow the modification of primitive data types
66 *** \param key The string or integer key of the variable to change
67 *** \param value The value to change the variable to
68 *** \note If the key could not be found in the active scope, no change will
69 *** occur and an error message will be logged.
70 **/
71 //@{
ModifyBool(const std::string & key,bool value)72 void ModifyBool(const std::string& key, bool value)
73 { _ModifyData(key, value); }
74
ModifyBool(int32 key,bool value)75 void ModifyBool(int32 key, bool value)
76 { _ModifyData(key, value); }
77
ModifyInt(const std::string & key,int32 value)78 void ModifyInt(const std::string& key, int32 value)
79 { _ModifyData(key, value); }
80
ModifyInt(int32 key,int32 value)81 void ModifyInt(int32 key, int32 value)
82 { _ModifyData(key, value); }
83
ModifyUInt(const std::string & key,uint32 value)84 void ModifyUInt(const std::string& key, uint32 value)
85 { _ModifyData(key, value); }
86
ModifyUInt(int32 key,uint32 value)87 void ModifyUInt(int32 key, uint32 value)
88 { _ModifyData(key, value); }
89
ModifyFloat(const std::string & key,float value)90 void ModifyFloat(const std::string& key, float value)
91 { _ModifyData(key, value); }
92
ModifyFloat(int32 key,float value)93 void ModifyFloat(int32 key, float value)
94 { _ModifyData(key, value); }
95
ModifyString(const std::string & key,const std::string & value)96 void ModifyString(const std::string& key, const std::string& value)
97 { _ModifyData(key, value); }
98
ModifyString(int32 key,const std::string & value)99 void ModifyString(int32 key, const std::string& value)
100 { _ModifyData(key, value); }
101 //@}
102
103 /** \name Data Addition Functions
104 *** \brief These functions allow the addition of new key/value pairs
105 *** \param key The string or integer key of the variable to add
106 *** \param value The value to set the new variable to
107 *** \note If the key already exists in the active scope when these
108 *** methods are called, the value of that key will be overwritten
109 *** with the new value. There will be no indication that this type of
110 *** overwrite operation took place, so be cautious.
111 **/
112 //@{
AddNewBool(const std::string & key,bool value)113 void AddNewBool(const std::string& key, bool value)
114 { _AddNewData(key, value); }
115
AddNewBool(int32 key,bool value)116 void AddNewBool(int32 key, bool value)
117 { _AddNewData(key, value); }
118
AddNewInt(const std::string & key,int32 value)119 void AddNewInt(const std::string& key, int32 value)
120 { _AddNewData(key, value); }
121
AddNewInt(int32 key,int32 value)122 void AddNewInt(int32 key, int32 value)
123 { _AddNewData(key, value); }
124
AddNewUInt(const std::string & key,uint32 value)125 void AddNewUInt(const std::string& key, uint32 value)
126 { _AddNewData(key, value); }
127
AddNewUInt(int32 key,uint32 value)128 void AddNewUInt(int32 key, uint32 value)
129 { _AddNewData(key, value); }
130
AddNewFloat(const std::string & key,float value)131 void AddNewFloat(const std::string& key, float value)
132 { _AddNewData(key, value); }
133
AddNewFloat(int32 key,float value)134 void AddNewFloat(int32 key, float value)
135 { _AddNewData(key, value); }
136
AddNewString(const std::string & key,const std::string & value)137 void AddNewString(const std::string& key, const std::string& value)
138 { _AddNewData(key, value); }
139
AddNewString(int32 key,const std::string & value)140 void AddNewString(int32 key, const std::string& value)
141 { _AddNewData(key, value); }
142 //@}
143
144 // TODO: Add methods to allow the creation of new tables
145 // void CreateNewTable(const std::string& key);
146 // void CreateNewTable(uint32 key);
147 // void EndNewTable();
148
149 /** \brief Commits all modified changes to the Lua file for permanent retention
150 *** \param leave_closed If set to true this file will be left closed once the function finishes (re-opens fileby default)
151 ***
152 *** This is a heavy-weight function because it has to write out the entire Lua state to a file,
153 *** regardless of whether one piece of data was modified or one hundred were. Therefore, you
154 *** should only call this function when all data modifications are done and you need to save the
155 *** result back to the hard-disk. Because the file referenced by this class is modified, the file
156 *** is closed and optionally re-opened before the function returns.
157 ***
158 *** \todo Spawn off this function in a seperate thread?
159 **/
160 void CommitChanges(bool leave_closed = false);
161
162 private:
163 /** \brief A helper function to CommitChanges() that writes out the contents of a table to the file
164 *** \param file A reference to the open file to write the table to
165 *** \param table A reference to the valid luabind::object that contains the table's contents
166 **/
167 void _CommitTable(WriteScriptDescriptor& file, const luabind::object& table);
168
169 /** \brief Template functions that update the key, value pair for the most recently opened table, or in the global scope
170 *** \param key The key name of the variable to be change
171 *** \param value The new value to set the key
172 **/
173 //@{
174 template <class T> void _ModifyData(const std::string& key, T value);
175 template <class T> void _ModifyData(int32 key, T value);
176 //@}
177
178 /** \brief Template functions which add a new key, value pair to the most recently opened scope
179 *** \param key The key name of the variable to add
180 *** \param value The value to set for the new variable
181 **/
182 //@{
183 template <class T> void _AddNewData(const std::string& key, T value);
184 template <class T> void _AddNewData(int32 key, T value);
185 //@}
186
187 /** \brief vector<string> iterator used to iterate over the list of open tables
188 ** This is stored here because it needs to persist over multiple function calls.
189 **/
190 std::vector<std::string>::iterator _open_tables_iterator;
191 }; // class ModifyScriptDescriptor
192
193 //-----------------------------------------------------------------------------
194 // Template Function Definitions
195 //-----------------------------------------------------------------------------
196
_ModifyData(const std::string & key,T value)197 template <class T> void ModifyScriptDescriptor::_ModifyData(const std::string& key, T value) {
198 // A pointer to the table where the object is contained
199 luabind::object* table = NULL;
200 // check to see if key is a table
201 std::string search_key = key;
202 int32 period = key.find('.');
203 std::string tablename;
204 std::vector<std::string> subkeys;
205 if (period != static_cast<int32>(std::string::npos)) {
206 // This key is a table with sub-keys
207 tablename = key.substr(0, period);
208 this->OpenTable(tablename);
209 int last = period;
210 while ((period = key.find('.', period + 1)) != static_cast<int32>(std::string::npos)) {
211 // push all subkeys into this table
212 subkeys.push_back(key.substr(last + 1, period));
213 this->OpenTable(key.substr(last+1, period));
214 last = period;
215 }
216 search_key = key.substr(last + 1);
217 }
218
219 if (_open_tables.empty() == true) // Retrieve the globals table
220 table = new luabind::object(luabind::from_stack(_lstack, LUA_GLOBALSINDEX));
221 else // Retrieve the most recently opened table from the top of the stack
222 table = new luabind::object(luabind::from_stack(_lstack, private_script::STACK_TOP));
223
224 if (luabind::type(*table) != LUA_TTABLE) {
225 _error_messages << "* _ModifyData() failed because it could not construct the table "
226 << "where the data resided: " << key << std::endl;
227 delete(table);
228 return;
229 }
230
231 for (luabind::iterator i(*table); i != private_script::TABLE_END; ++i) {
232 // Check to see if global value exists
233 try {
234 if (luabind::object_cast<std::string>(i.key()) == search_key) {
235 *i = value;
236 delete(table);
237 // close open tables
238 this->CloseTable();
239 return;
240 }
241 }
242 catch (...) {
243 // A cast failure does not indicate an error, simply continue on with the next key
244 }
245 }
246
247 // If the code reaches this far, then the variable could not be found
248 _error_messages << "* _ModifyData() failed because in the active scope, it did not find the "
249 << "table key: " << key << std::endl;
250 delete(table);
251 this->CloseTable();
252 } // template <class T> void ModifyScriptDescriptor::_ModifyData(const std::string& key, T value) {
253
254
255
_ModifyData(int32 key,T value)256 template <class T> void ModifyScriptDescriptor::_ModifyData(int32 key, T value) {
257 if (_open_tables.empty() == false) {
258 _error_messages << "* _ModifyData() failed because there were no open tables when the "
259 << "function was invoked for key: " << key << std::endl;
260 return;
261 }
262
263 luabind::object table(luabind::from_stack(_lstack, private_script::STACK_TOP));
264
265 if (luabind::type(table) != LUA_TTABLE) {
266 _error_messages << "* _ModifyData() failed because the top of the stack was not a table "
267 << "when trying to modify the data named: " << key << std::endl;
268 return;
269 }
270
271 for (luabind::iterator i(table); i != private_script::TABLE_END; ++i) {
272 // Check to see if global value exists
273 try {
274 if (luabind::object_cast<int32>(i.key()) == key) {
275 *i = value;
276 delete(table);
277 return;
278 }
279 }
280 catch (...) {
281 // A cast failure does not indicate an error, simply continue on with the next key
282 }
283 }
284
285 // If the code reaches this far, then the variable could not be found
286 _error_messages << "* _ModifyData() failed because in the active scope, it did not find the "
287 << "table key: " << key << std::endl;
288 delete(table);
289 } // template <class T> void ModifyScriptDescriptor::_ModifyData(int32 key, T value) {
290
291
292
_AddNewData(const std::string & key,T value)293 template <class T> void ModifyScriptDescriptor::_AddNewData(const std::string& key, T value) {
294 // A pointer to the table where the object is contained
295 luabind::object* table = NULL;
296
297 if (_open_tables.empty() == false) // Retrieve the globals table
298 table = new luabind::object(luabind::from_stack(_lstack, LUA_GLOBALSINDEX));
299 else // Retrieve the most recently opened table from the top of the stack
300 table = new luabind::object(luabind::from_stack(_lstack, private_script::STACK_TOP));
301
302 if (luabind::type(*table) != LUA_TTABLE) {
303 _error_messages << "* _AddNewData() failed because the top of the stack was not a table "
304 << "when trying to add the new data: " << key << std::endl;
305 return;
306 }
307
308 // NOTE: If the key already exists in the table, its value will be overwritten here
309 luabind::settable(*table, key, value);
310 } // template <class T> void ModifyScriptDescriptor::_AddNewData(const std::string& key, T value)
311
312
313
_AddNewData(int32 key,T value)314 template <class T> void ModifyScriptDescriptor::_AddNewData(int32 key, T value) {
315 if (_open_tables.empty() == false) {
316 _error_messages << "* _AddNewData() failed because there were no open tables when the "
317 << "function was invoked for key: " << key << std::endl;
318 return;
319 }
320
321 // A pointer to the table where the object is contained
322 luabind::object table(luabind::from_stack(_lstack, private_script::STACK_TOP));
323
324 if (luabind::type(table) != LUA_TTABLE) {
325 _error_messages << "* _AddNewData() failed because the top of the stack was not a table "
326 << "when trying to add the new data: " << key << std::endl;
327 return;
328 }
329
330 // NOTE: If the key already exists in the table, its value will be overwritten here
331 luabind::settable(table, key, value);
332 } // template <class T> void ModifyScriptDescriptor::_AddNewData(int32 key, T value)
333
334 } // namespace hoa_script
335
336 #endif // __SCRIPT_MODIFY_HEADER
337