1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "ConfigVariable.h"
4 #include "System/Log/ILog.h"
5 #include <iostream>
6 
7 using std::cout;
8 using std::map;
9 using std::string;
10 
11 /**
12  * @brief Log an error about a ConfigVariableMetaData
13  */
14 #define LOG_VAR(data, fmt, ...) \
15 	LOG_L(L_ERROR, "%s:%d: " fmt, data->GetDeclarationFile().Get().c_str(), data->GetDeclarationLine().Get(), ## __VA_ARGS__) \
16 
17 
GetMutableMetaDataMap()18 ConfigVariable::MetaDataMap& ConfigVariable::GetMutableMetaDataMap()
19 {
20 	static MetaDataMap vars;
21 	return vars;
22 }
23 
GetMetaDataMap()24 const ConfigVariable::MetaDataMap& ConfigVariable::GetMetaDataMap()
25 {
26 	return GetMutableMetaDataMap();
27 }
28 
AddMetaData(const ConfigVariableMetaData * data)29 void ConfigVariable::AddMetaData(const ConfigVariableMetaData* data)
30 {
31 	MetaDataMap& vars = GetMutableMetaDataMap();
32 	MetaDataMap::const_iterator pos = vars.find(data->GetKey());
33 
34 	if (pos != vars.end()) {
35 		LOG_VAR(data, "Duplicate config variable declaration \"%s\"", data->GetKey().c_str());
36 		LOG_VAR(pos->second, "  Previously declared here");
37 	}
38 	else {
39 		vars[data->GetKey()] = data;
40 	}
41 }
42 
GetMetaData(const string & key)43 const ConfigVariableMetaData* ConfigVariable::GetMetaData(const string& key)
44 {
45 	const MetaDataMap& vars = GetMetaDataMap();
46 	MetaDataMap::const_iterator pos = vars.find(key);
47 
48 	if (pos != vars.end()) {
49 		return pos->second;
50 	}
51 	else {
52 		return NULL;
53 	}
54 }
55 
56 #ifdef DEBUG
57 CONFIG(std::string, test)
58 	.defaultValue("x y z")
59 	.description("\"quoted\", escaped: \\, \b, \f, \n, \r, \t");
60 #endif
61 
62 
63 /**
64  * @brief Call Quote if type is not bool, float or int.
65  */
Quote(const string & type,const string & value)66 static inline string Quote(const string& type, const string& value)
67 {
68 	if (type == "bool" || type == "float" || type == "int") {
69 		return value;
70 	}
71 	else {
72 		return Quote(value);
73 	}
74 }
75 
76 /**
77  * @brief Write a ConfigVariableMetaData to a stream.
78  */
operator <<(std::ostream & out,const ConfigVariableMetaData * d)79 static std::ostream& operator<< (std::ostream& out, const ConfigVariableMetaData* d)
80 {
81 	const char* const OUTER_INDENT = "  ";
82 	const char* const INDENT = "    ";
83 
84 	out << OUTER_INDENT << Quote(d->GetKey()) << ": {\n";
85 
86 #define KV(key, value) out << INDENT << Quote(#key) << ": " << (value) << ",\n"
87 
88 	if (d->GetDeclarationFile().IsSet()) {
89 		KV(declarationFile, Quote(d->GetDeclarationFile().Get()));
90 	}
91 	if (d->GetDeclarationLine().IsSet()) {
92 		KV(declarationLine, d->GetDeclarationLine().Get());
93 	}
94 	if (d->GetDescription().IsSet()) {
95 		KV(description, Quote(d->GetDescription().Get()));
96 	}
97 	if (d->GetReadOnly().IsSet()) {
98 		KV(readOnly, d->GetReadOnly().Get());
99 	}
100 	if (d->GetDefaultValue().IsSet()) {
101 		KV(defaultValue, Quote(d->GetType(), d->GetDefaultValue().ToString()));
102 	}
103 	if (d->GetMinimumValue().IsSet()) {
104 		KV(minimumValue, Quote(d->GetType(), d->GetMinimumValue().ToString()));
105 	}
106 	if (d->GetMaximumValue().IsSet()) {
107 		KV(maximumValue, Quote(d->GetType(), d->GetMaximumValue().ToString()));
108 	}
109 	if (d->GetSafemodeValue().IsSet()) {
110 		KV(safemodeValue, Quote(d->GetType(), d->GetSafemodeValue().ToString()));
111 	}
112 	// Type is required.
113 	// Easiest to do this last because of the trailing comma that isn't there.
114 	out << INDENT << Quote("type") << ": " << Quote(d->GetType()) << "\n";
115 
116 #undef KV
117 
118 	out << OUTER_INDENT << "}";
119 
120 	return out;
121 }
122 
123 /**
124  * @brief Output config variable meta data as JSON to stdout.
125  *
126  * This can be tested using, for example:
127  *
128  *	./spring --list-config-vars |
129  *		python -c 'import json, sys; json.dump(json.load(sys.stdin), sys.stdout)'
130  */
OutputMetaDataMap()131 void ConfigVariable::OutputMetaDataMap()
132 {
133 	cout << "{\n";
134 
135 	const MetaDataMap& mdm = GetMetaDataMap();
136 	for (MetaDataMap::const_iterator it = mdm.begin(); it != mdm.end(); ++it) {
137 		if (it != mdm.begin()) {
138 			cout << ",\n";
139 		}
140 		cout << it->second;
141 	}
142 
143 	cout << "\n}\n";
144 }
145