|
11 | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | 12 | // See the License for the specific language governing permissions and
|
13 | 13 | // limitations under the License.
|
14 |
| - |
15 | 14 | #ifndef FIREBASE_DATABASE_CLIENT_CPP_SRC_DESKTOP_CORE_TREE_H_
|
16 | 15 | #define FIREBASE_DATABASE_CLIENT_CPP_SRC_DESKTOP_CORE_TREE_H_
|
17 | 16 |
|
18 | 17 | #include <map>
|
19 | 18 | #include <string>
|
| 19 | + |
20 | 20 | #include "app/src/optional.h"
|
21 | 21 | #include "app/src/path.h"
|
22 | 22 | #include "database/src/desktop/util_desktop.h"
|
@@ -151,6 +151,137 @@ class Tree {
|
151 | 151 | return SetValueAt(path, Optional<Value>(std::move(value)));
|
152 | 152 | }
|
153 | 153 |
|
| 154 | + // Returns the root-most element in the tree in the given path. For example, |
| 155 | + // if a Tree<int> contains the following values: |
| 156 | + // |
| 157 | + // -+ <root>: None |
| 158 | + // | |
| 159 | + // +- "Foo": None |
| 160 | + // | | |
| 161 | + // | +- "Bar": 100 |
| 162 | + // | | |
| 163 | + // | +- "Baz": 200 |
| 164 | + // | |
| 165 | + // +- "quux": 300 |
| 166 | + // |
| 167 | + // And the path given is "foo/bar/baz", then the return value will be a |
| 168 | + // pointer to 100, because that is the root-most value in the given path. If |
| 169 | + // a value cannot be found then nullptr is returned. |
| 170 | + const Value* RootMostValue(const Path& path) const { |
| 171 | + return RootMostValueMatching(path, [](const Value& value) { return true; }); |
| 172 | + } |
| 173 | + |
| 174 | + // Returns the root-most element in the tree in the given path that matches |
| 175 | + // the given predicate. For example, if a Tree<int> contains the following |
| 176 | + // values: |
| 177 | + // |
| 178 | + // -+ <root>: 0 |
| 179 | + // | |
| 180 | + // +- "Foo": 50 |
| 181 | + // | | |
| 182 | + // | +- "Bar": 100 |
| 183 | + // | | |
| 184 | + // | +- "Baz": 200 |
| 185 | + // | |
| 186 | + // +- "quux": 300 |
| 187 | + // |
| 188 | + // And the path given is "foo/bar/baz", and the predicate is value > 75, then |
| 189 | + // the return value will be a pointer to 100, because that is the root-most |
| 190 | + // value in the given path that meets the condition given. |
| 191 | + template <typename Func> |
| 192 | + const Value* RootMostValueMatching(const Path& path, |
| 193 | + const Func& predicate) const { |
| 194 | + if (value_.has_value() && predicate(*value_)) { |
| 195 | + return &value_.value(); |
| 196 | + } else { |
| 197 | + const Tree<Value>* current_tree = this; |
| 198 | + for (const std::string& directory : path.GetDirectories()) { |
| 199 | + current_tree = current_tree->GetChild(directory); |
| 200 | + if (current_tree == nullptr) { |
| 201 | + return nullptr; |
| 202 | + } else if (current_tree->value_.has_value() && |
| 203 | + predicate(*current_tree->value_)) { |
| 204 | + return ¤t_tree->value_.value(); |
| 205 | + } |
| 206 | + } |
| 207 | + return nullptr; |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + // Returns the leaf-most element in the tree in the given path. For example, |
| 212 | + // if a Tree<int> contains the following values: |
| 213 | + // |
| 214 | + // -+ <root>: None |
| 215 | + // | |
| 216 | + // +- "Foo": 200 |
| 217 | + // | | |
| 218 | + // | +- "Bar": 100 |
| 219 | + // | | |
| 220 | + // | +- "Baz": None |
| 221 | + // | |
| 222 | + // +- "quux": 300 |
| 223 | + // |
| 224 | + // And the path given is "foo/bar/baz", then the return value will be a |
| 225 | + // pointer to 100, because that is the leaf-most value in the given path. If |
| 226 | + // a value cannot be found then nullptr is returned. |
| 227 | + const Value* LeafMostValue(const Path& path) const { |
| 228 | + return LeafMostValueMatching(path, [](const Value& value) { return true; }); |
| 229 | + } |
| 230 | + |
| 231 | + // Returns the leaf-most element in the tree in the given path that matches |
| 232 | + // the given predicate. For example, if a Tree<int> contains the following |
| 233 | + // values: |
| 234 | + // |
| 235 | + // -+ <root>: 200 |
| 236 | + // | |
| 237 | + // +- "Foo": 100 |
| 238 | + // | | |
| 239 | + // | +- "Bar": 50 |
| 240 | + // | | |
| 241 | + // | +- "Baz": 0 |
| 242 | + // | |
| 243 | + // +- "quux": 300 |
| 244 | + // |
| 245 | + // And the path given is "foo/bar/baz", and the predicate is value > 75, then |
| 246 | + // the return value will be a pointer to 100, because that is the leaf-most |
| 247 | + // value in the given path that meets the condition given. |
| 248 | + template <typename Func> |
| 249 | + const Value* LeafMostValueMatching(const Path& path, |
| 250 | + const Func& predicate) const { |
| 251 | + const Value* current_value = |
| 252 | + (value_.has_value() && predicate(*value_)) ? &value_.value() : nullptr; |
| 253 | + const Tree<Value>* current_tree = this; |
| 254 | + for (const std::string& directory : path.GetDirectories()) { |
| 255 | + current_tree = current_tree->GetChild(directory); |
| 256 | + if (current_tree == nullptr) { |
| 257 | + return current_value; |
| 258 | + } else { |
| 259 | + if (current_tree->value_.has_value() && |
| 260 | + predicate(*current_tree->value_)) { |
| 261 | + current_value = ¤t_tree->value_.value(); |
| 262 | + } |
| 263 | + } |
| 264 | + } |
| 265 | + return current_value; |
| 266 | + } |
| 267 | + |
| 268 | + // Returns true if any location at or beneath this location in the tree meets |
| 269 | + // the criteria given by the predicate. |
| 270 | + template <typename Func> |
| 271 | + bool ContainsMatchingValue(const Func& predicate) const { |
| 272 | + if (value_.has_value() && predicate(*value_)) { |
| 273 | + return true; |
| 274 | + } else { |
| 275 | + for (auto& key_subtree_pair : children_) { |
| 276 | + const Tree<Value>& subtree = key_subtree_pair.second; |
| 277 | + if (subtree.ContainsMatchingValue(predicate)) { |
| 278 | + return true; |
| 279 | + } |
| 280 | + } |
| 281 | + return false; |
| 282 | + } |
| 283 | + } |
| 284 | + |
154 | 285 | // Get a child node using the given key.
|
155 | 286 | Tree<Value>* GetChild(const std::string& key) {
|
156 | 287 | if (key.empty()) {
|
|
0 commit comments