1 #include <hocon/config.hpp>
2 #include <hocon/config_parse_options.hpp>
3 #include <hocon/config_list.hpp>
4 #include <hocon/config_exception.hpp>
5 #include <internal/default_transformer.hpp>
6 #include <internal/resolve_context.hpp>
7 #include <internal/values/config_boolean.hpp>
8 #include <internal/values/config_null.hpp>
9 #include <internal/values/config_number.hpp>
10 #include <internal/values/config_double.hpp>
11 #include <internal/values/config_long.hpp>
12 #include <internal/values/config_int.hpp>
13 #include <internal/values/config_string.hpp>
14 #include <internal/values/simple_config_object.hpp>
15 #include <internal/parseable.hpp>
16 #include <internal/simple_includer.hpp>
17 
18 #include <boost/lexical_cast.hpp>
19 #include <boost/algorithm/string/trim.hpp>
20 #include <leatherman/util/environment.hpp>
21 #include <leatherman/locale/locale.hpp>
22 
23 #include <cfenv>
24 
25 // Mark string for translation (alias for leatherman::locale::format)
26 using leatherman::locale::_;
27 
28 using namespace std;
29 
30 namespace hocon {
31 
parse_file_any_syntax(std::string file_basename,config_parse_options options)32     shared_config config::parse_file_any_syntax(std::string file_basename, config_parse_options options) {
33         auto source = make_shared<file_name_source>();
34         return simple_includer::from_basename(move(source), move(file_basename), move(options))->to_config();
35     }
36 
parse_file_any_syntax(std::string file_basename)37     shared_config config::parse_file_any_syntax(std::string file_basename) {
38         return parse_file_any_syntax(move(file_basename), config_parse_options::defaults());
39     }
40 
parse_string(string s,config_parse_options options)41     shared_config config::parse_string(string s, config_parse_options options)
42     {
43         return parseable::new_string(move(s), move(options))->parse()->to_config();
44     }
45 
parse_string(string s)46     shared_config config::parse_string(string s)
47     {
48         return parse_string(move(s), config_parse_options());
49     }
50 
config(shared_object object)51     config::config(shared_object object) : _object(move(object)) { }
52 
root() const53     shared_object config::root() const {
54         return _object;
55     }
56 
origin() const57     shared_origin config::origin() const {
58         return _object->origin();
59     }
60 
resolve() const61     shared_config config::resolve() const {
62         return resolve(config_resolve_options());
63     }
64 
resolve(config_resolve_options options) const65     shared_config config::resolve(config_resolve_options options) const {
66         return resolve_with(shared_from_this(), move(options));
67     }
68 
resolve_with(shared_config source) const69     shared_config config::resolve_with(shared_config source) const {
70         return resolve_with(source, config_resolve_options());
71     }
72 
resolve_with(shared_config source,config_resolve_options options) const73     shared_config config::resolve_with(shared_config source, config_resolve_options options) const {
74         auto resolved = resolve_context::resolve(_object, source->_object, move(options));
75 
76         if (resolved == _object) {
77             return shared_from_this();
78         } else {
79             return make_shared<config>(dynamic_pointer_cast<const config_object>(resolved));
80         }
81     }
82 
has_path_peek(string const & path_expression) const83     shared_value config::has_path_peek(string const& path_expression) const {
84         path raw_path = path::new_path(path_expression);
85         shared_value peeked;
86         try {
87             peeked = _object->peek_path(raw_path);
88         } catch (config_exception& ex) {
89             if (_object->get_resolve_status() == resolve_status::RESOLVED) {
90                 throw ex;
91             }
92             throw config_exception(_("{1} has not been resolved, you need to call config::resolve()", raw_path.render()));
93         }
94         return peeked;
95     }
96 
has_path(string const & path_expression) const97     bool config::has_path(string const& path_expression) const {
98         shared_value peeked = has_path_peek(path_expression);
99         return peeked && peeked->value_type() != config_value::type::CONFIG_NULL;
100     }
101 
has_path_or_null(string const & path) const102     bool config::has_path_or_null(string const& path) const {
103         shared_value peeked = has_path_peek(path);
104         return peeked != nullptr;
105     }
106 
is_empty() const107     bool config::is_empty() const {
108         return _object->is_empty();
109     }
110 
find_paths(set<pair<string,shared_ptr<const config_value>>> & entries,path parent,shared_object obj)111     void config::find_paths(set<pair<string, shared_ptr<const config_value>>>& entries, path parent,
112                                    shared_object obj) {
113         for (auto&& entry : obj->entry_set()) {
114             string elem = entry.first;
115             shared_value v = entry.second;
116             path new_path = path::new_key(elem);
117             if (!parent.empty()) {
118                 new_path = new_path.prepend(parent);
119             }
120             if (auto object = dynamic_pointer_cast<const config_object>(v)) {
121                 find_paths(entries, new_path, object);
122             } else if (auto null_value = dynamic_pointer_cast<const config_null>(v)) {
123                 // nothing; nulls are conceptually not in a config
124             } else {
125                 entries.insert(make_pair(new_path.render(), v));
126             }
127         }
128     }
129 
entry_set() const130     set<pair<string, shared_ptr<const config_value>>> config::entry_set() const {
131         set<pair<string, shared_ptr<const config_value>>> entries;
132         find_paths(entries, path(), _object);
133         return entries;
134     }
135 
throw_if_null(shared_value v,config_value::type expected,path original_path)136     shared_value config::throw_if_null(shared_value v, config_value::type expected, path original_path) {
137         if (v->value_type() == config_value::type::CONFIG_NULL) {
138             // TODO Once we decide on a way of converting the type enum to a string, pass expected type string
139             throw null_exception(*(v->origin()), original_path.render());
140         } else {
141             return v;
142         }
143     }
144 
find_key(shared_object self,string const & key,config_value::type expected,path original_path)145     shared_value config::find_key(shared_object self, string const& key, config_value::type expected,
146                                          path original_path) {
147         return throw_if_null(find_key_or_null(self, key, expected, original_path), expected, original_path);
148     }
149 
find_key_or_null(shared_object self,string const & key,config_value::type expected,path original_path)150     shared_value config::find_key_or_null(shared_object self, string const& key, config_value::type expected,
151                                                  path original_path) {
152         shared_value v = self->peek_assuming_resolved(key, original_path);
153         if (!v) {
154             throw missing_exception(original_path.render());
155         }
156 
157         if (expected != config_value::type::UNSPECIFIED) {
158             v = default_transformer::transform(v, expected);
159         }
160 
161         if (expected != config_value::type::UNSPECIFIED &&
162                 v->value_type() != expected &&
163                 v->value_type() != config_value::type::CONFIG_NULL) {
164             throw wrong_type_exception(_("{1} could not be converted to the requested type", original_path.render()));
165         } else {
166             return v;
167         }
168     }
169 
find_or_null(shared_object self,path desired_path,config_value::type expected,path original_path)170     shared_value config::find_or_null(shared_object self, path desired_path,
171                                              config_value::type expected, path original_path) {
172         try {
173             string key = *desired_path.first();
174             path next = desired_path.remainder();
175             if (next.empty()) {
176                 return find_key_or_null(self, key, expected, original_path);
177             } else {
178                 shared_object o = dynamic_pointer_cast<const config_object>(
179                         find_key(self, key, config_value::type::OBJECT,
180                                  original_path.sub_path(0, original_path.length() - next.length())));
181                 return find_or_null(o, next, expected, original_path);
182             }
183         } catch (config_exception& ex) {
184             if (self->get_resolve_status() == resolve_status::RESOLVED) {
185                 throw ex;
186             }
187             throw config_exception(_("{1} has not been resolved, you need to call config::resolve()", desired_path.render()));
188         }
189     }
190 
find_or_null(string const & path_expression,config_value::type expected) const191     shared_value config::find_or_null(string const& path_expression, config_value::type expected) const {
192         path raw_path = path::new_path(path_expression);
193         return find_or_null(raw_path, expected, raw_path);
194     }
195 
find(string const & path_expression,config_value::type expected) const196     shared_value config::find(string const& path_expression, config_value::type expected) const {
197         path raw_path = path::new_path(path_expression);
198         return find(raw_path, expected, raw_path);
199     }
200 
get_is_null(string const & path_expression) const201     bool config::get_is_null(string const& path_expression) const {
202         shared_value v = find_or_null(path_expression, config_value::type::UNSPECIFIED);
203         return v->value_type() == config_value::type::CONFIG_NULL;
204     }
205 
get_value(string const & path_expression) const206     shared_value config::get_value(string const& path_expression) const {
207         return find(path_expression, config_value::type::UNSPECIFIED);
208     }
209 
get_bool(string const & path_expression) const210     bool config::get_bool(string const& path_expression) const {
211         shared_value v = find(path_expression, config_value::type::BOOLEAN);
212         return dynamic_pointer_cast<const config_boolean>(v)->bool_value();
213     }
214 
get_int(string const & path_expression) const215     int config::get_int(string const& path_expression) const {
216         shared_value v = find(path_expression, config_value::type::NUMBER);
217         return dynamic_pointer_cast<const config_number>(v)->int_value_range_checked(path_expression);
218     }
219 
get_long(string const & path_expression) const220     int64_t config::get_long(string const& path_expression) const {
221         shared_value v = find(path_expression, config_value::type::NUMBER);
222         return dynamic_pointer_cast<const config_number>(v)->long_value();
223     }
224 
get_double(string const & path_expression) const225     double config::get_double(string const& path_expression) const {
226         shared_value v = find(path_expression, config_value::type::NUMBER);
227         return dynamic_pointer_cast<const config_number>(v)->double_value();
228     }
229 
get_string(string const & path_expression) const230     string config::get_string(string const& path_expression) const {
231         shared_value v = find(path_expression, config_value::type::STRING);
232         return dynamic_pointer_cast<const config_string>(v)->transform_to_string();
233     }
234 
get_object(string const & path_expression) const235     shared_ptr<const config_object> config::get_object(string const& path_expression) const {
236         return dynamic_pointer_cast<const config_object>(find(path_expression, config_value::type::OBJECT));
237     }
238 
get_any_ref(string const & path_expression) const239     unwrapped_value config::get_any_ref(string const& path_expression) const {
240         return find(path_expression, config_value::type::UNSPECIFIED)->unwrapped();
241     }
242 
get_config(string const & path_expression) const243     shared_config config::get_config(string const& path_expression) const {
244         return get_object(path_expression)->to_config();
245     }
246 
get_list(string const & path_expression) const247     shared_list config::get_list(string const& path_expression) const {
248         return dynamic_pointer_cast<const config_list>(find(path_expression, config_value::type::LIST));
249     }
250 
get_bool_list(string const & path) const251     vector<bool> config::get_bool_list(string const& path) const {
252         return get_homogeneous_unwrapped_list<bool>(path);
253     }
254 
get_int_list(std::string const & path) const255     std::vector<int> config::get_int_list(std::string const& path) const {
256         return get_homogeneous_unwrapped_list<int>(path);
257     }
258 
get_long_list(std::string const & path) const259     std::vector<int64_t> config::get_long_list(std::string const& path) const {
260         return get_homogeneous_unwrapped_list<int64_t>(path);
261     }
262 
get_double_list(std::string const & path) const263     std::vector<double> config::get_double_list(std::string const& path) const {
264         return get_homogeneous_unwrapped_list<double>(path);
265     }
266 
get_string_list(std::string const & path) const267     std::vector<std::string> config::get_string_list(std::string const& path) const {
268         return get_homogeneous_unwrapped_list<string>(path);
269     }
270 
get_object_list(std::string const & path) const271     std::vector<shared_object> config::get_object_list(std::string const& path) const {
272         auto list = get_list(path);
273         vector<shared_object> object_list;
274         for (auto item : *list) {
275             shared_object obj = dynamic_pointer_cast<const config_object>(item);
276             if (obj == nullptr) {
277                 throw new config_exception(_("List does not contain only config_objects."));
278             }
279             object_list.push_back(obj);
280         }
281         return object_list;
282     }
283 
get_config_list(std::string const & path) const284     std::vector<shared_config> config::get_config_list(std::string const& path) const {
285         auto list = get_list(path);
286         vector<shared_config> object_list;
287         for (auto item : *list) {
288             shared_config obj = dynamic_pointer_cast<const config>(item);
289             if (obj == nullptr) {
290                 throw config_exception(_("List does not contain only configs."));
291             }
292             object_list.push_back(obj);
293         }
294         return object_list;
295     }
296 
297     template<>
get_homogeneous_unwrapped_list(std::string const & path) const298     std::vector<int64_t> config::get_homogeneous_unwrapped_list(std::string const& path) const {
299         auto list = boost::get<std::vector<unwrapped_value>>(get_list(path)->unwrapped());
300         std::vector<int64_t> long_list;
301         for (auto item : list) {
302             // Even if the parser stored the number as an int, we want to treat it as a long.
303             try {
304                 long_list.push_back(boost::get<int64_t>(item));
305             } catch (std::exception& ex) {
306                 try {
307                     long_list.push_back(boost::get<int>(item));
308                 } catch (boost::bad_get &ex) {
309                     throw config_exception(_("The list did not contain only the desired type."));
310                 }
311             }
312         }
313         return long_list;
314     }
315 
get_duration(string const & path) const316     duration config::get_duration(string const& path) const {
317         auto v = get_value(path);
318         if (auto d = dynamic_pointer_cast<const config_double>(v)) {
319             return convert(d->double_value(), time_unit::MILLISECONDS);
320         } else if (auto l = dynamic_pointer_cast<const config_long>(v)) {
321             return convert(l->long_value(), time_unit::MILLISECONDS);
322         } else if (auto i = dynamic_pointer_cast<const config_int>(v)) {
323             return convert(i->long_value(), time_unit::MILLISECONDS);
324         } else if (auto str = dynamic_pointer_cast<const config_string>(v)) {
325             return parse_duration(str->transform_to_string(), str->origin(), path);
326         } else {
327             throw bad_value_exception(*v->origin(), path, _("Value at '{1}' was not a number or string.", path));
328         }
329     }
330 
get_duration(string const & path,time_unit unit) const331     int64_t config::get_duration(string const& path, time_unit unit) const {
332         auto timespan = get_duration(path);
333         int64_t result = 0;
334         switch (unit) {
335             case time_unit::NANOSECONDS:
336                 result = (timespan.first * 1000000000) + timespan.second;
337                 break;
338             case time_unit::MICROSECONDS:
339                 result = (timespan.first * 1000000) + (timespan.second / 1000);
340                 break;
341             case time_unit::MILLISECONDS:
342                 result = (timespan.first * 1000) + (timespan.second / 1000000);
343                 break;
344             case time_unit::SECONDS:
345                 result = timespan.first;
346                 break;
347             case time_unit::MINUTES:
348                 result = timespan.first / 60;
349                 break;
350             case time_unit::HOURS:
351                 result = timespan.first / 3600;
352                 break;
353             case time_unit::DAYS:
354                 result = timespan.first / 86400;
355                 break;
356             default:
357                 throw config_exception(_("Not a valid time_unit"));
358         }
359         if ((result >= 0) != (timespan.first >= 0)) {
360             throw config_exception(_("as_long: Overflow occurred during time conversion"));
361         }
362         return result;
363     }
364 
convert(int64_t number,time_unit units)365     duration config::convert(int64_t number, time_unit units) {
366         int64_t seconds = 0;
367         int nanos = 0;
368         switch (units) {
369             case time_unit::NANOSECONDS:
370                 seconds = number / 1000000000;
371                 nanos = number % 1000000000;
372                 break;
373             case time_unit::MICROSECONDS:
374                 seconds = number / 1000000;
375                 nanos = (number % 1000000) * 1000;
376                 break;
377             case time_unit::MILLISECONDS:
378                 seconds = number / 1000;
379                 nanos = (number % 1000) * 1000000;
380                 break;
381             case time_unit::SECONDS:
382                 seconds = number;
383                 break;
384             case time_unit::MINUTES:
385                 seconds = number * 60;
386                 break;
387             case time_unit::HOURS:
388                 seconds = number * 3600;
389                 break;
390             case time_unit::DAYS:
391                 seconds = number * 86400;
392                 break;
393             default:
394                 throw config_exception(_("Not a valid time_unit"));
395         }
396         if ((number >= 0) != (seconds >= 0)) {
397             throw config_exception(_("convert_long: Overflow occurred during time conversion"));
398         }
399         return duration(seconds, nanos);
400     }
401 
convert(double number,time_unit units)402     duration config::convert(double number, time_unit units) {
403         double seconds = 0;
404         double nanos = 0;
405         switch (units) {
406             case time_unit::NANOSECONDS:
407                 seconds = number / 1000000000;
408                 nanos = fmod(number, 1000000000);
409                 break;
410             case time_unit::MICROSECONDS:
411                 seconds = number / 1000000;
412                 nanos = fmod(number, 1000000) * 1000;
413                 break;
414             case time_unit::MILLISECONDS:
415                 seconds = number / 1000;
416                 nanos = fmod(number, 1000) * 1000000;
417                 break;
418             case time_unit::SECONDS:
419                 seconds = number;
420                 nanos = fmod(number, 1) * 1000000000;
421                 break;
422             case time_unit::MINUTES:
423                 seconds = number * 60;
424                 nanos = fmod(number * 60,  1) * 1000000000;
425                 break;
426             case time_unit::HOURS:
427                 seconds = number * 3600;
428                 nanos = fmod(number * 3600, 1) * 1000000000;
429                 break;
430             case time_unit::DAYS:
431                 seconds = number * 86400;
432                 nanos = fmod(number * 86400, 1) * 1000000000;
433                 break;
434             default:
435                 throw config_exception(_("Not a valid time_unit"));
436         }
437         if (!isnormal(seconds) || !isnormal(nanos)) {
438             throw config_exception(_("convert_double: Overflow occurred during time conversion"));
439         }
440         return duration(static_cast<int64_t>(seconds), static_cast<int>(nanos));
441     }
442 
get_units(string const & unit_string)443     time_unit config::get_units(string const& unit_string) {
444         if (unit_string == "ns" || unit_string == "nanos" || unit_string == "nanoseconds") {
445             return time_unit::NANOSECONDS;
446         } else if (unit_string == "us" || unit_string == "micros" || unit_string == "microseconds") {
447             return time_unit::MICROSECONDS;
448         } else if (unit_string.empty() || unit_string == "ms" || unit_string == "millis" || unit_string == "milliseconds") {
449             return time_unit::MILLISECONDS;
450         } else if (unit_string == "s" || unit_string == "seconds") {
451             return time_unit::SECONDS;
452         } else if (unit_string == "m" || unit_string == "minutes") {
453             return time_unit::MINUTES;
454         } else if (unit_string == "h" || unit_string == "hours") {
455             return time_unit::HOURS;
456         } else if (unit_string == "d" || unit_string == "days") {
457             return time_unit::DAYS;
458         } else {
459             throw config_exception(_("Could not parse time unit '{1}' (try ns, us, ms, s, m, h, or d)", unit_string));
460         }
461     }
462 
parse_duration(string input,shared_origin origin_for_exception,string path_for_exception)463     duration config::parse_duration(string input, shared_origin origin_for_exception, string path_for_exception) {
464         boost::algorithm::trim(input);
465         string original_unit_string = boost::algorithm::trim_left_copy_if(input, !boost::algorithm::is_alpha());
466         string unit_string = original_unit_string;
467         string number_string = boost::algorithm::trim_copy(input.substr(0, input.length() - unit_string.length()));
468 
469         if (number_string.empty()) {
470             throw bad_value_exception(*origin_for_exception, path_for_exception, _("No number in duration value '{1}'", input));
471         }
472 
473         if (unit_string.length() > 2 && unit_string.back() != 's') {
474             unit_string += "s";
475         }
476 
477         try {
478             int64_t number = boost::lexical_cast<int64_t>(number_string);
479             return convert(number, get_units(unit_string));
480         } catch (boost::bad_lexical_cast& ex) {
481             try {
482                 double number = boost::lexical_cast<double>(number_string);
483                 return convert(number, get_units(unit_string));
484             }
485             catch (boost::bad_lexical_cast& ex) {
486                 throw bad_value_exception(*origin_for_exception, path_for_exception, _("Value '{1}' could not be converted to a number.", number_string));
487             }
488         }
489     }
490 
to_fallback_value() const491     shared_value config::to_fallback_value() const {
492         return _object;
493     }
494 
with_fallback(shared_ptr<const config_mergeable> other) const495     shared_ptr<const config_mergeable> config::with_fallback(shared_ptr<const config_mergeable> other) const {
496         if (auto newobj = dynamic_pointer_cast<const config_object>(_object->with_fallback(other))) {
497             return newobj->to_config();
498         } else {
499             throw bug_or_broken_exception(_("Creating new object from config_object did not return a config_object"));
500         }
501     }
502 
operator ==(config const & other) const503     bool config::operator==(config const &other) const {
504         return _object == other._object;
505     }
506 
with_value(string const & path_expression,shared_ptr<const config_value> value) const507     shared_config config::with_value(string const& path_expression, shared_ptr<const config_value> value) const {
508         path raw_path = path::new_path(path_expression);
509         return make_shared<config>(root()->with_value(raw_path, value));
510     }
511 
is_resolved() const512     bool config::is_resolved() const {
513         return root()->get_resolve_status() == resolve_status::RESOLVED;
514     }
515 
without_path(string const & path_expression) const516     shared_config config::without_path(string const& path_expression) const {
517         path raw_path = path::new_path(path_expression);
518         return make_shared<config>(root()->without_path(raw_path));
519     }
520 
with_only_path(string const & path_expression) const521     shared_config config::with_only_path(string const& path_expression) const {
522         path raw_path = path::new_path(path_expression);
523         return make_shared<config>(root()->with_only_path(raw_path));
524     }
525 
at_key(shared_origin origin,string const & key) const526     shared_config config::at_key(shared_origin origin, string const& key) const {
527         return root()->at_key(origin, key);
528     }
529 
at_key(std::string const & key) const530     shared_config config::at_key(std::string const& key) const {
531         return root()->at_key(key);
532     }
533 
at_path(std::string const & path) const534     shared_config config::at_path(std::string const& path) const {
535         return root()->at_path(path);
536     }
537 
default_includer()538     shared_includer config::default_includer() {
539         static auto _default_includer = make_shared<simple_includer>(nullptr);
540         return _default_includer;
541     }
542 
check_valid(shared_config reference,std::vector<std::string> restrict_to_paths) const543     void config::check_valid(shared_config reference, std::vector<std::string> restrict_to_paths) const {
544         // TODO: implement this once resolve functionality is working
545         throw runtime_error(_("Method not implemented"));
546     }
547 
peek_path(path desired_path) const548     shared_value config::peek_path(path desired_path) const {
549         return root()->peek_path(desired_path);
550     }
551 
find_or_null(path path_expression,config_value::type expected,path original_path) const552     shared_value config::find_or_null(path path_expression, config_value::type expected,
553             path original_path) const {
554         return find_or_null(_object, path_expression, expected, original_path);
555     }
556 
find(path path_expression,config_value::type expected,path original_path) const557     shared_value config::find(path path_expression, config_value::type expected, path original_path) const {
558         return throw_if_null(find_or_null(_object, path_expression, expected, original_path), expected, original_path);
559     }
560 
env_variables_as_config_object()561     shared_object config::env_variables_as_config_object() {
562         unordered_map<string, shared_value> values;
563         leatherman::util::environment::each([&](string& k, string& v) {
564             auto origin = make_shared<simple_config_origin>("env var " + k);
565             values.emplace(k, make_shared<config_string>(origin, v, config_string_type::QUOTED));
566             return true;
567         });
568         auto origin = make_shared<simple_config_origin>("env variables");
569         return make_shared<simple_config_object>(origin, move(values), resolve_status::RESOLVED, false);
570     }
571 
572 
573 }  // namespace hocon
574