1 #include <internal/values/config_delayed_merge_object.hpp>
2 #include <internal/values/config_delayed_merge.hpp>
3 #include <internal/values/simple_config_list.hpp>
4 #include <internal/resolve_result.hpp>
5 #include <hocon/config_exception.hpp>
6 #include <leatherman/locale/locale.hpp>
7 
8 // Mark string for translation (alias for leatherman::locale::format)
9 using leatherman::locale::_;
10 
11 using namespace std;
12 
13 namespace hocon {
14 
config_delayed_merge_object(shared_origin origin,vector<shared_value> const & stack)15     config_delayed_merge_object::config_delayed_merge_object(shared_origin origin, vector<shared_value> const &stack) :
16         config_object(move(origin)), _stack(move(stack)) {
17         if (_stack.empty()) {
18             throw config_exception(_("creating empty delayed merge object"));
19         }
20 
21         if (!dynamic_pointer_cast<const config_object>(_stack.front())) {
22             throw config_exception(_("created a delayed merge object not guaranteed to be an object"));
23         }
24 
25         for (auto& v : _stack) {
26             if (dynamic_pointer_cast<const config_delayed_merge>(v) || dynamic_pointer_cast<const config_delayed_merge_object>(v)) {
27                 throw config_exception(_("placed nested delayed_merge in a config_delayed_merge_object, should have consolidated stack"));
28             }
29         }
30     }
31 
resolve_substitutions(resolve_context const & context,resolve_source const & source) const32     resolve_result<shared_value> config_delayed_merge_object::resolve_substitutions(resolve_context const& context,
33                                                                                     resolve_source const& source) const {
34         return config_delayed_merge::resolve_substitutions(dynamic_pointer_cast<const replaceable_merge_stack>(shared_from_this()), _stack, context, source);
35     }
36 
unmerged_values() const37     vector<shared_value> config_delayed_merge_object::unmerged_values() const {
38         return _stack;
39     }
40 
make_replacement(resolve_context const & context,int skipping) const41     shared_value config_delayed_merge_object::make_replacement(resolve_context const &context, int skipping) const {
42         return config_delayed_merge::make_replacement(move(context), _stack, move(skipping));
43     }
44 
with_value(path raw_path,shared_value value) const45     shared_object config_delayed_merge_object::with_value(path raw_path, shared_value value) const {
46         throw not_resolved();
47     }
48 
with_value(string key,shared_value value) const49     shared_object config_delayed_merge_object::with_value(string key, shared_value value) const {
50         throw not_resolved();
51     }
52 
new_copy(resolve_status const & status,shared_origin origin) const53     shared_object config_delayed_merge_object::new_copy(resolve_status const& status, shared_origin origin) const {
54         if (status != get_resolve_status()) {
55             throw bug_or_broken_exception(_("attempt to create resolved config_delayted_merge_object"));
56         }
57         return make_shared<config_delayed_merge_object>(move(origin), _stack);
58     }
59 
unwrapped() const60     unwrapped_value config_delayed_merge_object::unwrapped() const {
61         throw config_exception(_("need to config::resolve before using this object, see the API docs."));
62     }
63 
attempt_peek_with_partial_resolve(string const & key) const64     shared_value config_delayed_merge_object::attempt_peek_with_partial_resolve(string const& key) const {
65         /* a partial resolve of a ConfigDelayedMergeObject always results in a
66          * SimpleConfigObject because all the substitutions in the stack get
67          * resolved in order to look up the partial.
68          * So we know here that we have not been resolved at all even
69          * partially.
70          * Given that, all this code is probably gratuitous, since the app code
71          * is likely broken. But in general we only throw NotResolved if you try
72          * to touch the exact key that isn't resolved, so this is in that
73          * spirit
74          */
75 
76         // we'll be able to return a key if we have a value that ignores
77         // fallbacks, prior to any unmergeable values.
78         for (auto&& layer : _stack) {
79             if (auto object_layer = dynamic_pointer_cast<const config_object>(layer)) {
80                 auto v = object_layer->attempt_peek_with_partial_resolve(key);
81                 if (v != nullptr) {
82                     if (v->ignores_fallbacks()) {
83                         // we know we won't need to merge anything in to this value
84                         return v;
85                     } else {
86                         /* we can't return this value because we know there are
87                          * unmergeable values later in the stack that may
88                          * contain values that need to be merged with this
89                          * value. we'll throw the exception when we get to those
90                          * unmergeable values, so continue here.
91                          */
92                         continue;
93                     }
94                 } else if (dynamic_pointer_cast<const unmergeable>(layer)) {
95                     /* an unmergeable object (which would be another
96                      * config_delayed_merge_object) can't know that a key is
97                      * missing, so it can't return null; it can only return a
98                      * value or throw not_possible_to_resolve
99                      */
100                     throw bug_or_broken_exception(_("should not be reached: unmergeable object returned null value"));
101                 } else {
102                     /* a non-unmergeable config_objeect that returned null
103                      * for the key in question is not relevant, we can keep
104                      * looking for a value.
105                      */
106                     continue;
107                 }
108             } else if (dynamic_pointer_cast<const unmergeable>(layer)) {
109                 throw not_resolved_exception(_("Key '{1}' is not available at '{2}' because value at '{3}' has not been resolved and may turn out to contain or hide '{4}'. Be sure to config::resolve() before using a config object", key, origin()->description(), layer->origin()->description(), key));
110             } else if (layer->get_resolve_status() == resolve_status::UNRESOLVED) {
111                 /* if the layer is not an object, and not a substitution or
112                  * merge, then it's something that's unresolved because it _contains_
113                  * an unresolved object... i.e. it's an array
114                  */
115                 if (!dynamic_pointer_cast<const simple_config_list>(layer)) {
116                     throw bug_or_broken_exception(_("Expecting a list here, not {1}", layer->render()));
117                     // all later objects will be hidden so we can say we won't find the key
118                     return nullptr;
119                 }
120             } else {
121                 /* non-object, but resolved, like an integer or something.
122                  * has no children so the one we're after won't be in it.
123                  * we would only have this in the stack in case something
124                  * else "looks back" to it due to a cycle.
125                  * anyway at this point we know we can't find the key anymore.
126                  */
127                 if (!layer->ignores_fallbacks()) {
128                     throw bug_or_broken_exception(_("resolved non-object should ignore fallbacks"));
129                 }
130                 return nullptr;
131             }
132         }
133         /* If we get here, then we never found anything unresolved which means
134          * the ConfigDelayedMergeObject should not have existed. some
135          * invariant was violated.
136          */
137         throw bug_or_broken_exception(_("Delayed merge stack does not contain any unmergeable values"));
138     }
139 
entry_set() const140     unordered_map<string, shared_value> const& config_delayed_merge_object::entry_set() const {
141         throw not_resolved();
142     }
143 
without_path(path raw_path) const144     shared_object config_delayed_merge_object::without_path(path raw_path) const {
145         throw not_resolved();
146     }
147 
with_only_path(path raw_path) const148     shared_object config_delayed_merge_object::with_only_path(path raw_path) const {
149         throw not_resolved();
150     }
151 
with_only_path_or_null(path raw_path) const152     shared_object config_delayed_merge_object::with_only_path_or_null(path raw_path) const {
153         throw not_resolved();
154     }
155 
operator ==(config_value const & other) const156     bool config_delayed_merge_object::operator==(config_value const& other) const {
157         return equals<config_delayed_merge_object>(other, [&](config_delayed_merge_object const& o) { return _stack == o._stack; });
158     }
159 
ignores_fallbacks() const160     bool config_delayed_merge_object::ignores_fallbacks() const {
161         return _stack.back()->ignores_fallbacks();
162     }
163 
not_resolved() const164     not_resolved_exception config_delayed_merge_object::not_resolved() const {
165         return not_resolved_exception(_("need to config::resolve() before using this object, see the API docs for config::resolve()"));
166     }
167 
replace_child(shared_value const & child,shared_value replacement) const168     shared_value config_delayed_merge_object::replace_child(shared_value const& child, shared_value replacement) const
169     {
170         auto new_stack = replace_child_in_list(_stack, child, move(replacement));
171         if (new_stack.empty()) {
172              return nullptr;
173         } else {
174             return make_shared<config_delayed_merge>(origin(), new_stack);
175         }
176     }
177 
has_descendant(shared_value const & descendant) const178     bool config_delayed_merge_object::has_descendant(shared_value const& descendant) const
179     {
180         return has_descendant_in_list(_stack, descendant);
181     }
182 
render(string & s,int indent,bool at_root,string const & at_key,config_render_options options) const183     void config_delayed_merge_object::render(string& s, int indent, bool at_root, string const& at_key, config_render_options options) const {
184         config_delayed_merge::render(_stack, s, indent, at_root, at_key, options);
185     }
186 
render(string & s,int indent,bool at_root,config_render_options options) const187     void config_delayed_merge_object::render(string& s, int indent, bool at_root, config_render_options options) const {
188         render(s, indent, at_root, "", options);
189     }
190 }  // namespace hocon
191