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