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