reference_cache.h 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #pragma once
  2. #include <functional>
  3. #include <map>
  4. #include <optional>
  5. #include <jvalidate/detail/pointer.h>
  6. #include <jvalidate/detail/reference.h>
  7. namespace jvalidate::detail {
  8. class ReferenceCache {
  9. private:
  10. std::map<Reference, RootReference, std::greater<>> to_anchor_;
  11. std::map<RootReference, Reference, std::greater<>> to_absolute_;
  12. public:
  13. void emplace(URI const & root) { to_absolute_.emplace(root, root); }
  14. Reference emplace(Reference const & absolute, RootReference const & canonical) {
  15. for (Reference where = absolute; not where.pointer().empty();) {
  16. // Recursively add all possible alternative paths that are equivalent to
  17. // this absolute path as valid mappings to this canonical one.
  18. auto it = to_absolute_.lower_bound(where.root());
  19. if (it == to_absolute_.end() || where.root() == it->second.root()) {
  20. break;
  21. }
  22. if (auto ptr = it->second.pointer(); where.pointer().starts_with(ptr)) {
  23. where = it->second / where.pointer().relative_to(ptr);
  24. to_anchor_.emplace(where, canonical);
  25. } else {
  26. break;
  27. }
  28. }
  29. to_anchor_.emplace(absolute, canonical);
  30. // TODO: Validate uniqueness?
  31. to_absolute_.emplace(canonical, absolute);
  32. return Reference(canonical);
  33. }
  34. Reference relative_to_nearest_anchor(Reference const & ref,
  35. bool for_parent_reference = false) const {
  36. if (to_absolute_.contains(ref.root())) {
  37. return ref;
  38. }
  39. auto it = for_parent_reference ? to_anchor_.upper_bound(ref) : to_anchor_.lower_bound(ref);
  40. if (it == to_anchor_.end()) {
  41. return ref;
  42. }
  43. auto const & [absolute, anchor] = *it;
  44. if (not ref.pointer().starts_with(absolute.pointer())) {
  45. return ref;
  46. }
  47. return Reference(anchor, ref.pointer().relative_to(absolute.pointer()));
  48. }
  49. URI actual_parent_uri(detail::Reference const & parent) const {
  50. // TODO(samjaffe): Think about this some more - there's something awkward here
  51. URI uri = relative_to_nearest_anchor(parent, true).uri();
  52. if (not uri.empty() || not parent.uri().empty()) {
  53. return uri;
  54. }
  55. if (auto it = to_anchor_.find(Reference()); it != to_anchor_.end()) {
  56. return it->second.uri();
  57. }
  58. return uri;
  59. }
  60. };
  61. }