reference_cache.h 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  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) {
  14. to_absolute_.emplace(root, root);
  15. to_anchor_.emplace(root, root);
  16. }
  17. Reference emplace(Reference const & absolute, RootReference const & canonical) {
  18. for (Reference where = absolute; not where.pointer().empty();) {
  19. // Recursively add all possible alternative paths that are equivalent to
  20. // this absolute path as valid mappings to this canonical one.
  21. auto it = to_absolute_.lower_bound(where.root());
  22. if (it == to_absolute_.end() || where.root() == it->second.root()) {
  23. break;
  24. }
  25. if (auto ptr = it->second.pointer(); where.pointer().starts_with(ptr)) {
  26. where = it->second / where.pointer().relative_to(ptr);
  27. to_anchor_.emplace(where, canonical);
  28. } else {
  29. break;
  30. }
  31. }
  32. to_anchor_.emplace(absolute, canonical);
  33. to_absolute_.emplace(canonical, absolute);
  34. return Reference(canonical);
  35. }
  36. Reference relative_to_nearest_anchor(Reference const & ref,
  37. bool for_parent_reference = false) const {
  38. auto it = for_parent_reference ? to_anchor_.upper_bound(ref) : to_anchor_.lower_bound(ref);
  39. if (it == to_anchor_.end()) {
  40. return ref;
  41. }
  42. auto const & [absolute, anchor] = *it;
  43. if (not ref.pointer().starts_with(absolute.pointer())) {
  44. return ref;
  45. }
  46. return Reference(anchor, ref.pointer().relative_to(absolute.pointer()));
  47. }
  48. URI actual_parent_uri(detail::Reference const & parent) const {
  49. // TODO(samjaffe): Think about this some more - there's something awkward here
  50. URI uri = relative_to_nearest_anchor(parent, true).uri();
  51. if (not uri.empty() || not parent.uri().empty()) {
  52. return uri;
  53. }
  54. if (auto it = to_anchor_.find(Reference()); it != to_anchor_.end()) {
  55. return it->second.uri();
  56. }
  57. return uri;
  58. }
  59. };
  60. }