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