1 #include <hocon/config_value.hpp> 2 #include <internal/config_util.hpp> 3 #include <hocon/config_object.hpp> 4 #include <hocon/config_exception.hpp> 5 #include <internal/values/simple_config_object.hpp> 6 #include <internal/simple_config_origin.hpp> 7 #include <internal/container.hpp> 8 #include <internal/values/config_delayed_merge.hpp> 9 #include <internal/resolve_result.hpp> 10 #include <leatherman/locale/locale.hpp> 11 #include <algorithm> 12 13 // Mark string for translation (alias for leatherman::locale::format) 14 using leatherman::locale::_; 15 16 using namespace std; 17 18 namespace hocon { 19 config_value(shared_origin origin)20 config_value::config_value(shared_origin origin) : 21 _origin(move(origin)) { } 22 transform_to_string() const23 string config_value::transform_to_string() const { 24 return ""; 25 } 26 origin() const27 shared_origin const& config_value::origin() const { 28 return _origin; 29 } 30 get_resolve_status() const31 resolve_status config_value::get_resolve_status() const { 32 return resolve_status::RESOLVED; 33 } 34 render() const35 string config_value::render() const { 36 return render(config_render_options()); 37 } 38 render(config_render_options options) const39 string config_value::render(config_render_options options) const { 40 string result; 41 render(result, 0, true, "", options); 42 return result; 43 } 44 resolve_status_from_values(std::vector<shared_value> const & values)45 resolve_status resolve_status_from_values(std::vector<shared_value> const& values) { 46 for (auto& v : values) { 47 if (v->get_resolve_status() == resolve_status::UNRESOLVED) { 48 return resolve_status::UNRESOLVED; 49 } 50 } 51 return resolve_status::RESOLVED; 52 } 53 render(std::string & result,int indent,bool at_root,std::string const & at_key,config_render_options options) const54 void config_value::render(std::string &result, int indent, bool at_root, std::string const& at_key, config_render_options options) const { 55 if (!at_key.empty()) { 56 string rendered_key; 57 if (options.get_json()) { 58 rendered_key = render_json_string(at_key); 59 } else { 60 rendered_key = render_string_unquoted_if_possible(at_key); 61 } 62 63 result += rendered_key; 64 if (options.get_json()) { 65 result += options.get_formatted() ? " : " : ":"; 66 } else { 67 // in non-JSON we can omit the colon or equals before an object 68 if (dynamic_cast<const config_object*>(this)) { 69 if (options.get_formatted()) { 70 result += " "; 71 } 72 } else { 73 result += "="; 74 } 75 } 76 } 77 render(result, indent, at_root, options); 78 } 79 render(std::string & result,int indent,bool at_root,config_render_options options) const80 void config_value::render(std::string &result, int indent, bool at_root, 81 config_render_options options) const { 82 result += transform_to_string(); 83 } 84 indent(std::string & result,int indent,config_render_options const & options)85 void config_value::indent(std::string &result, int indent, config_render_options const& options) { 86 if (options.get_formatted()) { 87 result.append(indent*4, ' '); 88 } 89 } 90 at_path(shared_origin origin,path raw_path) const91 shared_config config_value::at_path(shared_origin origin, path raw_path) const { 92 path parent = raw_path.parent(); 93 shared_config result = at_key(origin, *raw_path.last()); 94 while (!parent.empty()) { 95 string key = *parent.last(); 96 result = result->at_key(origin, key); 97 parent = parent.parent(); 98 } 99 return result; 100 } 101 at_key(shared_origin origin,std::string const & key) const102 shared_config config_value::at_key(shared_origin origin, std::string const& key) const { 103 unordered_map<string, shared_value> map { make_pair(key, shared_from_this()) }; 104 return simple_config_object(origin, map).to_config(); 105 } 106 at_key(std::string const & key) const107 shared_config config_value::at_key(std::string const& key) const { 108 return at_key(make_shared<simple_config_origin>("at_key(" + key + ")"), key); 109 } 110 at_path(std::string const & path_expression) const111 shared_config config_value::at_path(std::string const& path_expression) const { 112 shared_origin origin = make_shared<simple_config_origin>("at_path(" + path_expression + ")"); 113 return at_path(move(origin), path::new_path(path_expression)); 114 } 115 with_origin(shared_origin origin) const116 shared_value config_value::with_origin(shared_origin origin) const { 117 if (_origin == origin) { 118 return shared_from_this(); 119 } else { 120 return new_copy(move(origin)); 121 } 122 } 123 124 resolve_result<shared_value> resolve_substitutions(resolve_context const & context,resolve_source const & source) const125 config_value::resolve_substitutions(resolve_context const& context, 126 resolve_source const& source) const 127 { 128 return resolve_result<shared_value>(context, shared_from_this()); 129 } 130 replace_child_in_list(std::vector<shared_value> const & values,shared_value const & child,shared_value replacement)131 std::vector<shared_value> config_value::replace_child_in_list(std::vector<shared_value> const& values, 132 shared_value const& child, shared_value replacement) 133 { 134 vector<shared_value> new_list = values; 135 auto it = find(new_list.begin(), new_list.end(), child); 136 assert(it != values.end()); 137 138 if (replacement) { 139 *it = move(replacement); 140 } else { 141 new_list.erase(it); 142 } 143 return new_list; 144 } 145 has_descendant_in_list(std::vector<shared_value> const & values,shared_value const & descendant)146 bool config_value::has_descendant_in_list(std::vector<shared_value> const& values, shared_value const& descendant) 147 { 148 // Simple breadth-first-search for descendants 149 // We look for the same object via pointer comparison, not equivalent objects. 150 if (find(values.begin(), values.end(), descendant) != values.end()) { 151 return true; 152 } 153 154 for (auto& v : values) { 155 if (auto c = dynamic_pointer_cast<const container>(v)) { 156 if (c->has_descendant(descendant)) { 157 return true; 158 } 159 } 160 } 161 162 return false; 163 } 164 no_exceptions_modifier(string prefix)165 config_value::no_exceptions_modifier::no_exceptions_modifier(string prefix): _prefix(std::move(prefix)) {} 166 modify_child_may_throw(string const & key_or_null,shared_value v)167 shared_value config_value::no_exceptions_modifier::modify_child_may_throw(string const& key_or_null, shared_value v) { 168 try { 169 return modify_child(key_or_null, v); 170 } catch (runtime_error& e) { 171 throw e; 172 } catch (exception& e) { 173 throw config_exception(_("Unexpected exception:{1}", e.what())); 174 } 175 } 176 modify_child(string const & key,shared_value v) const177 shared_value config_value::no_exceptions_modifier::modify_child(string const& key, shared_value v) const { 178 return v->relativized(_prefix); 179 } 180 with_fallback(std::shared_ptr<const config_mergeable> mergeable) const181 shared_ptr<const config_mergeable> config_value::with_fallback(std::shared_ptr<const config_mergeable> mergeable) const { 182 if (ignores_fallbacks()) { 183 return shared_from_this(); 184 } else { 185 auto other = dynamic_pointer_cast<const config_mergeable>(mergeable)->to_fallback_value(); 186 187 if (auto unmergeable_other = dynamic_pointer_cast<const unmergeable>(other)) { 188 return merged_with_the_unmergeable(unmergeable_other); 189 } else if (auto object_other = dynamic_pointer_cast<const config_object>(other)) { 190 return merged_with_object(object_other); 191 } else { 192 return merged_with_non_object(dynamic_pointer_cast<const config_value>(other)); 193 } 194 } 195 } 196 require_not_ignoring_fallbacks() const197 void config_value::require_not_ignoring_fallbacks() const { 198 if (ignores_fallbacks()) { 199 throw config_exception(_("method should not have been called with ignores_fallbacks=true")); 200 } 201 } 202 ignores_fallbacks() const203 bool config_value::ignores_fallbacks() const { 204 return get_resolve_status() == resolve_status::RESOLVED; 205 } 206 with_fallbacks_ignored() const207 shared_value config_value::with_fallbacks_ignored() const { 208 if (ignores_fallbacks()) { 209 return shared_from_this(); 210 } else { 211 throw config_exception(_("value class doesn't implement forced fallback-ignoring")); 212 } 213 } 214 construct_delayed_merge(shared_origin origin,std::vector<shared_value> stack) const215 shared_value config_value::construct_delayed_merge(shared_origin origin, std::vector<shared_value> stack) const { 216 return make_shared<config_delayed_merge>(move(origin), move(stack)); 217 } 218 merged_with_the_unmergeable(std::vector<shared_value> stack,std::shared_ptr<const unmergeable> fallback) const219 shared_value config_value::merged_with_the_unmergeable(std::vector<shared_value> stack, 220 std::shared_ptr<const unmergeable> fallback) const { 221 require_not_ignoring_fallbacks(); 222 223 // if we turn out to be an object, and the fallback also does, 224 // then a merge may be required; delay until we resolve. 225 226 auto unmerged_values = fallback->unmerged_values(); 227 stack.insert(stack.end(), make_move_iterator(unmerged_values.begin()), make_move_iterator(unmerged_values.end())); 228 auto merged = config_object::merge_origins(stack); 229 return construct_delayed_merge(merged, move(stack)); 230 } 231 merged_with_the_unmergeable(std::shared_ptr<const unmergeable> fallback) const232 shared_value config_value::merged_with_the_unmergeable(std::shared_ptr<const unmergeable> fallback) const { 233 require_not_ignoring_fallbacks(); 234 235 return merged_with_the_unmergeable({ shared_from_this() }, move(fallback)); 236 } 237 merged_with_object(vector<shared_value> stack,shared_object fallback) const238 shared_value config_value::merged_with_object(vector<shared_value> stack, shared_object fallback) const { 239 require_not_ignoring_fallbacks(); 240 241 if (dynamic_cast<const config_object*>(this)) { 242 throw config_exception(_("Objects must reimplement merged_with_object")); 243 } 244 245 return merged_with_non_object(move(stack), move(fallback)); 246 } 247 merged_with_non_object(vector<shared_value> stack,shared_value fallback) const248 shared_value config_value::merged_with_non_object(vector<shared_value> stack, shared_value fallback) const { 249 require_not_ignoring_fallbacks(); 250 251 if (get_resolve_status() == resolve_status::RESOLVED) { 252 // falling back to a non-object doesn't merge anything, and also 253 // prohibits merging any objects that we fall back to later. 254 // so we have to switch to ignoresFallbacks mode. 255 return with_fallbacks_ignored(); 256 } else { 257 // if unresolved, we may have to look back to fallbacks as part of 258 // the resolution process, so always delay 259 return delay_merge(move(stack), move(fallback)); 260 } 261 } 262 merged_with_non_object(shared_value fallback) const263 shared_value config_value::merged_with_non_object(shared_value fallback) const { 264 require_not_ignoring_fallbacks(); 265 266 return merged_with_non_object({shared_from_this()}, move(fallback)); 267 } 268 merged_with_object(shared_object fallback) const269 shared_value config_value::merged_with_object(shared_object fallback) const { 270 require_not_ignoring_fallbacks(); 271 272 return merged_with_object({shared_from_this()}, move(fallback)); 273 } 274 to_fallback_value() const275 shared_value config_value::to_fallback_value() const { 276 return shared_from_this(); 277 } 278 delay_merge(std::vector<shared_value> stack,shared_value fallback) const279 shared_value config_value::delay_merge(std::vector<shared_value> stack, shared_value fallback) const { 280 // if we turn out to be an object, and the fallback also does, 281 // then a merge may be required. 282 // if we contain a substitution, resolving it may need to look 283 // back to the fallback. 284 stack.push_back(move(fallback)); 285 auto merged = config_object::merge_origins(stack); 286 return construct_delayed_merge(merged, move(stack)); 287 } 288 289 } // namespace hocon 290