#pragma once #include #include #include #include #include namespace jvalidate::detail { class ReferenceCache { private: std::map> to_anchor_; std::map> to_absolute_; public: void emplace(URI const & root) { to_absolute_.emplace(root, root); to_anchor_.emplace(root, root); } Reference emplace(Reference const & absolute, RootReference const & canonical) { for (Reference where = absolute; not where.pointer().empty();) { // Recursively add all possible alternative paths that are equivalent to // this absolute path as valid mappings to this canonical one. auto it = to_absolute_.lower_bound(where.root()); if (it == to_absolute_.end() || where.root() == it->second.root()) { break; } if (auto ptr = it->second.pointer(); where.pointer().starts_with(ptr)) { where = it->second / where.pointer().relative_to(ptr); to_anchor_.emplace(where, canonical); } else { break; } } to_anchor_.emplace(absolute, canonical); to_absolute_.emplace(canonical, absolute); return Reference(canonical); } Reference relative_to_nearest_anchor(Reference const & ref, bool for_parent_reference = false) const { auto it = for_parent_reference ? to_anchor_.upper_bound(ref) : to_anchor_.lower_bound(ref); if (it == to_anchor_.end()) { return ref; } auto const & [absolute, anchor] = *it; if (not ref.pointer().starts_with(absolute.pointer())) { return ref; } return Reference(anchor, ref.pointer().relative_to(absolute.pointer())); } URI actual_parent_uri(detail::Reference const & parent) const { // TODO(samjaffe): Think about this some more - there's something awkward here URI uri = relative_to_nearest_anchor(parent, true).uri(); if (not uri.empty() || not parent.uri().empty()) { return uri; } if (auto it = to_anchor_.find(Reference()); it != to_anchor_.end()) { return it->second.uri(); } return uri; } }; }