| // Copyright 2026 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "formats/bmff/xpath.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/strings/strip.h" |
| |
| namespace credentio { |
| namespace { |
| // Returns true if the given path matches the pattern. Like, |
| // "/a[1]" matches "/a[1]"; |
| // "/a[1]" also matches "/a"; |
| // but "/a[1]" doesn't match "/a[2]". |
| // Similarly, "/a[1]/b[2]/c[3]" matches "/a/b/c", "/a[1]/b/c", "/a/b[2]/c", |
| // "/a/b/c[3]", etc; |
| // but "/a[2]/b[2]/c[3]" doesn't match "/a[1]/b/c". |
| // Note that while patterns like "/a/b/c" are expected, we don't expect to see |
| // paths like "/a/b/c" because positional indexes should always be applied in |
| // the path. |
| bool MatchXPath(absl::string_view path, absl::string_view pattern) { |
| if (path == pattern) { |
| return true; |
| } |
| std::vector<absl::string_view> terms_a = absl::StrSplit(path, '/'); |
| std::vector<absl::string_view> terms_b = absl::StrSplit(pattern, '/'); |
| if (terms_a.size() != terms_b.size()) { |
| return false; |
| } |
| for (int i = 0; i < terms_a.size(); ++i) { |
| if (terms_a[i] == terms_b[i]) { |
| continue; |
| } |
| if (absl::StrContains(terms_b[i], "[")) { |
| // Term b is a positioned element; requires exact match. |
| return false; |
| } |
| absl::string_view remainder = terms_a[i]; |
| if (absl::ConsumePrefix(&remainder, terms_b[i]) && |
| absl::StartsWith(remainder, "[")) { |
| continue; |
| } |
| return false; |
| } |
| return true; |
| }; |
| } // namespace |
| |
| bool XPathMatcher::Matches(absl::string_view path) const { |
| return MatchXPath(path, pattern_); |
| } |
| |
| std::string XPath::ToString() const { |
| std::string result; |
| for (const XPath* cursor = this; cursor != nullptr; |
| cursor = cursor->parent_) { |
| std::string element_name = |
| absl::StrCat("/", cursor->name_, |
| cursor->position_.has_value() |
| ? absl::StrCat("[", *cursor->position_, "]") |
| : ""); |
| result = absl::StrCat(element_name, result); |
| } |
| return result; |
| } |
| |
| } // namespace credentio |