1 /* This file is part of the Pangolin Project.
2 * http://github.com/stevenlovegrove/Pangolin
3 *
4 * Copyright (c) 2011 Steven Lovegrove
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following
13 * conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28 #include <pangolin/var/varextra.h>
29 #include <pangolin/var/varstate.h>
30 #include <pangolin/utils/file_utils.h>
31 #include <pangolin/utils/picojson.h>
32 #include <pangolin/utils/transform.h>
33
34 #include <iostream>
35 #include <fstream>
36
37 using namespace std;
38
39 namespace pangolin
40 {
41
I()42 VarState& VarState::I() {
43 static VarState singleton;
44 return singleton;
45 }
46
VarState()47 VarState::VarState()
48 : varHasChanged(false)
49 {
50 }
51
~VarState()52 VarState::~VarState() {
53 Clear();
54 }
55
Clear()56 void VarState::Clear() {
57 for(VarStoreContainer::iterator i = vars.begin(); i != vars.end(); ++i) {
58 delete i->second;
59 }
60 vars.clear();
61 var_adds.clear();
62 }
63
ProcessHistoricCallbacks(NewVarCallbackFn callback,void * data,const std::string & filter)64 void ProcessHistoricCallbacks(NewVarCallbackFn callback, void* data, const std::string& filter)
65 {
66 for (VarState::VarStoreAdditions::const_iterator i = VarState::I().var_adds.begin(); i != VarState::I().var_adds.end(); ++i)
67 {
68 const std::string& name = *i;
69 if (StartsWith(name, filter)) {
70 callback(data, name, *VarState::I()[name], false);
71 }
72 }
73 }
74
RegisterNewVarCallback(NewVarCallbackFn callback,void * data,const std::string & filter)75 void RegisterNewVarCallback(NewVarCallbackFn callback, void* data, const std::string& filter)
76 {
77 VarState::I().new_var_callbacks.push_back(NewVarCallback(filter,callback,data));
78 }
79
RegisterGuiVarChangedCallback(GuiVarChangedCallbackFn callback,void * data,const std::string & filter)80 void RegisterGuiVarChangedCallback(GuiVarChangedCallbackFn callback, void* data, const std::string& filter)
81 {
82 VarState::I().gui_var_changed_callbacks.push_back(GuiVarChangedCallback(filter,callback,data));
83 }
84
85 // Recursively expand val
ProcessVal(const string & val)86 string ProcessVal(const string& val )
87 {
88 return Transform(val, [](const std::string& k) -> std::string {
89 if( VarState::I().Exists(k) ) {
90 return VarState::I()[k]->str->Get();
91 }else{
92 return std::string("#");
93 }
94 });
95 }
96
AddVar(const std::string & name,const string & val)97 void AddVar(const std::string& name, const string& val )
98 {
99 const std::string full = ProcessVal(val);
100
101 VarValueGeneric*& v = VarState::I()[name];
102 if(!v) {
103 VarValue<std::string>* nv = new VarValue<std::string>(val);
104 InitialiseNewVarMetaGeneric<std::string>(*nv, name);
105 v = nv;
106 }
107 v->str->Set(full);
108 }
109
110 #ifdef ALIAS
AddAlias(const string & alias,const string & name)111 void AddAlias(const string& alias, const string& name)
112 {
113 std::map<std::string,_Var*>::iterator vi = vars.find(name);
114
115 if( vi != vars.end() )
116 {
117 _Var * v = vi->second;
118 vars[alias].create(v->val,v->val_default,v->type_name);
119 vars[alias].Meta().friendly = alias;
120 v->generic = false;
121 vars[alias].generic = false;
122 }else{
123 pango_print_error("Variable %s does not exist to alias.\n", name);
124 }
125 }
126 #endif
127
ParseVarsFile(const string & filename)128 void ParseVarsFile(const string& filename)
129 {
130 ifstream f(filename.c_str());
131
132 if( f.is_open() )
133 {
134 while( !f.bad() && !f.eof())
135 {
136 const int c = f.peek();
137
138 if( isspace(c) )
139 {
140 // ignore leading whitespace
141 f.get();
142 }else{
143 if( c == '#' || c == '%' )
144 {
145 // ignore lines starting # or %
146 string comment;
147 getline(f,comment);
148 }else{
149 // Otherwise, find name and value, seperated by '=' and ';'
150 string name;
151 string val;
152 getline(f,name,'=');
153 getline(f,val,';');
154 name = Trim(name, " \t\n\r");
155 val = Trim(val, " \t\n\r");
156
157 if( name.size() >0 && val.size() > 0 )
158 {
159 if( !val.substr(0,1).compare("@") )
160 {
161 #ifdef ALIAS
162 AddAlias(name,val.substr(1));
163 #endif
164 }else{
165 AddVar(name,val);
166 }
167 }
168 }
169 }
170 }
171 f.close();
172 }else{
173 cerr << "Unable to open '" << filename << "' for configuration data" << endl;
174 }
175 }
176
177 PANGOLIN_EXPORT
LoadJsonFile(const std::string & filename,const string & prefix)178 void LoadJsonFile(const std::string& filename, const string &prefix)
179 {
180 bool some_change = false;
181
182 picojson::value file_json(picojson::object_type,true);
183 std::ifstream f(filename);
184 if(f.is_open()) {
185 const std::string err = picojson::parse(file_json,f);
186 if(err.empty()) {
187 if(file_json.contains("vars") ) {
188 picojson::value vars = file_json["vars"];
189 if(vars.is<picojson::object>()) {
190 for(picojson::object::iterator
191 i = vars.get<picojson::object>().begin();
192 i!= vars.get<picojson::object>().end();
193 ++i)
194 {
195 const std::string& name = i->first;
196 if(pangolin::StartsWith(name, prefix)) {
197 const std::string& val = i->second.get<std::string>();
198
199 VarValueGeneric*& v = VarState::I()[name];
200 if(!v) {
201 VarValue<std::string>* nv = new VarValue<std::string>(val);
202 InitialiseNewVarMetaGeneric<std::string>(*nv, name);
203 v = nv;
204 }else{
205 v->str->Set(val);
206 v->Meta().gui_changed = true;
207 }
208 some_change = true;
209 }
210 }
211 }
212 }
213 }else{
214 pango_print_error("%s\n", err.c_str());
215 }
216 }else{
217 pango_print_error("Unable to load vars from %s\n", filename.c_str());
218 }
219
220 if(some_change) {
221 FlagVarChanged();
222 }
223 }
224
225 PANGOLIN_EXPORT
SaveJsonFile(const std::string & filename,const string & prefix)226 void SaveJsonFile(const std::string& filename, const string &prefix)
227 {
228 picojson::value vars(picojson::object_type,true);
229
230 for(VarState::VarStoreAdditions::const_iterator
231 i = VarState::I().var_adds.begin();
232 i != VarState::I().var_adds.end();
233 ++i)
234 {
235 const std::string& name = *i;
236 if(StartsWith(name,prefix)) {
237 try{
238 const std::string val = VarState::I()[name]->str->Get();
239 vars[name] = val;
240 }catch(const BadInputException&)
241 {
242 // Ignore things we can't serialise
243 }
244 }
245 }
246
247 picojson::value file_json(picojson::object_type,true);
248 file_json["pangolin_version"] = PANGOLIN_VERSION_STRING;
249 file_json["vars"] = vars;
250
251 std::ofstream f(filename);
252 if(f.is_open()) {
253 f << file_json.serialize(true);
254 }else{
255 pango_print_error("Unable to serialise to %s\n", filename.c_str());
256 }
257 }
258
259 }
260