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