1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2011 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #include <platform/cbassert.h>
18 #include <cstdio>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <strings.h>
22 #include <cerrno>
23 #include <sys/stat.h>
24 #include <string>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include <map>
29 
30 #include <ctype.h>
31 
32 #include "cJSON.h"
33 
34 using namespace std;
35 
36 stringstream prototypes;
37 stringstream initialization;
38 stringstream implementation;
39 
40 typedef string (*getValidatorCode)(const std::string &, cJSON*);
41 
42 std::map<string, getValidatorCode> validators;
43 map<string, string> getters;
44 map<string, string> datatypes;
45 
46 
operator <<(std::ostream &out, const cJSON *o)47 static std::ostream& operator <<(std::ostream &out, const cJSON *o) {
48     switch (o->type) {
49     case cJSON_Number:
50         if (o->valueint != o->valuedouble) {
51             out << (float)o->valuedouble;
52         } else {
53             out << o->valueint;
54         }
55         break;
56     case cJSON_String:
57         out << '"' << o->valuestring << '"';
58         break;
59     default:
60         cerr << "Internal error.. unknow json code" << endl;
61         abort();
62     }
63     return out;
64 }
65 
isFloat(const cJSON *o)66 static bool isFloat(const cJSON *o) {
67     return o->valueint != o->valuedouble;
68 }
69 
getRangeValidatorCode(const std::string &key, cJSON *o)70 static string getRangeValidatorCode(const std::string &key, cJSON *o) {
71     // the range validator should contain a "min" and "max" element
72     cJSON *min = cJSON_GetObjectItem(o, "min");
73     cJSON *max = cJSON_GetObjectItem(o, "max");
74 
75     if (min == 0 || max == 0) {
76         cerr << "Incorrect syntax for a range validator specified for"
77              << "\"" << key << "\"." << endl
78              <<"You need both a min and max clause." << endl;
79         exit(1);
80     }
81 
82     if (min->type != max->type || min->type != cJSON_Number) {
83         cerr << "Incorrect datatype for the range validator specified for "
84              << "\"" << key << "\"." << endl
85              << "Only numbers are supported." << endl;
86         exit(1);
87     }
88 
89     stringstream ss;
90     if (isFloat(min) || isFloat(max)) {
91         ss << "(new FloatRangeValidator())->min((float)" << min << ")->max((float)" << max << ")";
92     } else {
93         ss << "(new SizeRangeValidator())->min(" << min << ")->max(" << max << ")";
94     }
95 
96     return ss.str();
97 }
98 
getEnumValidatorCode(const std::string &key, cJSON *o)99 static string getEnumValidatorCode(const std::string &key, cJSON *o) {
100 
101     if (o->type != cJSON_Array) {
102         cerr << "Incorrect enum value for " << key
103              << ".  Array of values is required." << endl;
104         exit(1);
105     }
106 
107     if (cJSON_GetArraySize(o) < 1) {
108         cerr << "At least one validator enum element is required ("
109              << key << ")" << endl;
110         exit(1);
111     }
112 
113     stringstream ss;
114     ss << "(new EnumValidator())";
115 
116     for (cJSON *p(o->child); p; p = p->next) {
117         if (p->type != cJSON_String) {
118             cerr << "Incorrect validator for " << key
119                  << ", all enum entries must be strings." << endl;
120             exit(1);
121         }
122         char *value = cJSON_Print(p);
123         ss << "\n\t\t->add(" << value << ")";
124         free(value);
125     }
126     return ss.str();
127 }
128 
initialize()129 static void initialize() {
130     prototypes
131         << "/*" << endl
132         << " *     Copyright 2011 Couchbase, Inc" << endl
133         << " *" << endl
134         << " *   Licensed under the Apache License, Version 2.0 (the \"License\");" << endl
135         << " *   you may not use this file except in compliance with the License." << endl
136         << " *   You may obtain a copy of the License at" << endl
137         << " *" << endl
138         << " *       http://www.apache.org/licenses/LICENSE-2.0" << endl
139         << " *" << endl
140         << " *   Unless required by applicable law or agreed to in writing, software" << endl
141         << " *   distributed under the License is distributed on an \"AS IS\" BASIS," << endl
142         << " *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied." << endl
143         << " *   See the License for the specific language governing permissions and" << endl
144         << " *   limitations under the License." << endl
145         << " */" << endl
146         << endl
147         << "// ###########################################" << endl
148         << "// # DO NOT EDIT! THIS IS A GENERATED FILE " << endl
149         << "// ###########################################" << endl
150         << "#ifndef SRC_GENERATED_CONFIGURATION_H_" << endl
151         << "#define SRC_GENERATED_CONFIGURATION_H_ 1" << endl
152         << endl
153         << "#include \"config.h\"" << endl
154         << endl
155         << "#include <string>" << endl;
156 
157     implementation
158         << "/*" << endl
159         << " *     Copyright 2011 Couchbase, Inc" << endl
160         << " *" << endl
161         << " *   Licensed under the Apache License, Version 2.0 (the \"License\");" << endl
162         << " *   you may not use this file except in compliance with the License." << endl
163         << " *   You may obtain a copy of the License at" << endl
164         << " *" << endl
165         << " *       http://www.apache.org/licenses/LICENSE-2.0" << endl
166         << " *" << endl
167         << " *   Unless required by applicable law or agreed to in writing, software" << endl
168         << " *   distributed under the License is distributed on an \"AS IS\" BASIS," << endl
169         << " *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied." << endl
170         << " *   See the License for the specific language governing permissions and" << endl
171         << " *   limitations under the License." << endl
172         << " */" << endl
173         << endl
174         << "// ###########################################" << endl
175         << "// # DO NOT EDIT! THIS IS A GENERATED FILE " << endl
176         << "// ###########################################" << endl
177         << endl
178         << "#include \"config.h\"" << endl
179         << "#include \"configuration.h\"" << endl;
180     validators["range"] = getRangeValidatorCode;
181     validators["enum"] = getEnumValidatorCode;
182     getters["std::string"] = "getString";
183     getters["bool"] = "getBool";
184     getters["size_t"] = "getInteger";
185     getters["ssize_t"] = "getSignedInteger";
186     getters["float"] = "getFloat";
187     datatypes["bool"] = "bool";
188     datatypes["size_t"] = "size_t";
189     datatypes["ssize_t"] = "ssize_t";
190     datatypes["float"] = "float";
191     datatypes["string"] = "std::string";
192     datatypes["std::string"] = "std::string";
193 }
194 
getString(cJSON *i)195 static string getString(cJSON *i) {
196     if (i == NULL) {
197         return "";
198     }
199     cb_assert(i->type == cJSON_String);
200     return i->valuestring;
201 }
202 
isReadOnly(cJSON *o)203 static bool isReadOnly(cJSON *o) {
204     cJSON *i = cJSON_GetObjectItem(o, "dynamic");
205     if (i == NULL || i->type == cJSON_False) {
206         return false;
207     }
208 
209     cb_assert(i->type == cJSON_True);
210     return true;
211 }
212 
getDatatype(const std::string &key, cJSON *o)213 static string getDatatype(const std::string &key, cJSON *o) {
214     cJSON *i = cJSON_GetObjectItem(o, "type");
215     cb_assert(i != NULL && i->type == cJSON_String);
216     string ret = i->valuestring;
217 
218     map<string, string>::iterator iter = datatypes.find(ret);
219     if (iter == datatypes.end()) {
220         cerr << "Invalid datatype specified for \"" << key << "\": "
221              << i->valuestring << endl;
222         exit(1);
223     }
224 
225     return iter->second;
226 }
227 
getValidator(const std::string &key, cJSON *o)228 static string getValidator(const std::string &key, cJSON *o) {
229     if (o == NULL) {
230         return "";
231     }
232 
233     cJSON *n = cJSON_GetArrayItem(o, 0);
234     if (n == NULL) {
235         return "";
236     }
237 
238     std::map<string, getValidatorCode>::iterator iter;
239     iter = validators.find(string(n->string));
240     if (iter == validators.end()) {
241         cerr << "Unknown validator specified for \"" << key
242              << "\": \"" << n->string << "\""
243              << endl;
244         exit(1);
245     }
246 
247     return (iter->second)(key, n);
248 }
249 
getGetterPrefix(const string &str)250 static string getGetterPrefix(const string &str) {
251     if (str.compare("bool") == 0) {
252         return "is";
253     } else {
254         return "get";
255     }
256 }
257 
getCppName(const string &str)258 static string getCppName(const string &str) {
259     stringstream ss;
260     bool doUpper = true;
261 
262     string::const_iterator iter;
263     for (iter = str.begin(); iter != str.end(); ++iter) {
264         if (*iter == '_') {
265             doUpper = true;
266         } else {
267             if (doUpper) {
268                 ss << (char)toupper(*iter);
269                 doUpper = false;
270             } else {
271                 ss << (char)*iter;
272             }
273         }
274     }
275     return ss.str();
276 }
277 
generate(cJSON *o)278 static void generate(cJSON *o) {
279     cb_assert(o != NULL);
280 
281     string config_name = o->string;
282     string cppname = getCppName(config_name);
283     string type = getDatatype(config_name, o);
284     string defaultVal = getString(cJSON_GetObjectItem(o, "default"));
285 
286     if (defaultVal.compare("max") == 0 || defaultVal.compare("min") == 0) {
287         if (type.compare("std::string") != 0) {
288             stringstream ss;
289             ss << "std::numeric_limits<" << type << ">::" << defaultVal << "()";
290             defaultVal = ss.str();
291         }
292     }
293 
294     string validator = getValidator(config_name,
295                                     cJSON_GetObjectItem(o, "validator"));
296 
297     // Generate prototypes
298     prototypes << "    " << type
299                << " " << getGetterPrefix(type)
300                << cppname << "() const;" << endl;
301     if  (!isReadOnly(o)) {
302         prototypes << "    void set" << cppname << "(const " << type
303                    << " &nval);" << endl;
304     }
305 
306     // Generate initialization code
307     initialization << "    setParameter(\"" << config_name << "\", ";
308     if (type.compare("std::string") == 0) {
309         initialization << "(const char*)\"" << defaultVal << "\");" << endl;
310     } else {
311         initialization << "(" << type << ")" << defaultVal << ");" << endl;
312     }
313     if (!validator.empty()) {
314         initialization << "    setValueValidator(\"" << config_name
315                        << "\", " << validator << ");" << endl;
316     }
317 
318 
319     // Generate the getter
320     implementation << type << " Configuration::" << getGetterPrefix(type)
321                    << cppname << "() const {" << endl
322                    << "    return " << getters[type] << "(\""
323                    << config_name << "\");" << endl << "}" << endl;
324 
325     if  (!isReadOnly(o)) {
326         // generate the setter
327         implementation << "void Configuration::set" << cppname
328                        << "(const " << type << " &nval) {" << endl
329                        << "    setParameter(\"" << config_name
330                        << "\", nval);" << endl
331                        << "}" << endl;
332     }
333 }
334 
335 /**
336  * Read "configuration.json" and generate getters and setters
337  * for the parameters in there
338  */
main(int argc, char **argv)339 int main(int argc, char **argv) {
340     const char *file = "configuration.json";
341     if (argc == 2) {
342         file = argv[1];
343     }
344 
345     initialize();
346 
347     struct stat st;
348     if (stat(file, &st) == -1) {
349         cerr << "Failed to look up " << file << ": "
350              << strerror(errno) << endl;
351         return 1;
352     }
353 
354     char *data = new char[st.st_size + 1];
355     data[st.st_size] = 0;
356     ifstream input(file);
357     input.read(data, st.st_size);
358     input.close();
359 
360     cJSON *c = cJSON_Parse(data);
361     if (c == NULL) {
362         cerr << "Failed to parse JSON.. probably syntax error" << endl;
363         return 1;
364     }
365 
366     cJSON *params = cJSON_GetObjectItem(c, "params");
367     if (params == NULL) {
368         cerr << "FATAL: could not find \"params\" section" << endl;
369         return 1;
370     }
371 
372     int num = cJSON_GetArraySize(params);
373     for (int ii = 0; ii < num; ++ii) {
374         generate(cJSON_GetArrayItem(params, ii));
375     }
376     prototypes << "#endif  // SRC_GENERATED_CONFIGURATION_H_" << endl;
377 
378     ofstream headerfile("src/generated_configuration.h");
379     headerfile << prototypes.str();
380     headerfile.close();
381 
382     ofstream implfile("src/generated_configuration.cc");
383     implfile << implementation.str() << endl
384              << "void Configuration::initialize() {" << endl
385              << initialization.str()
386              << "}" << endl;
387     implfile.close();
388 
389     cJSON_Delete(c);
390     delete []data;
391 
392     return 0;
393 }
394