All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.database.core.utilities.Tree Maven / Gradle / Ivy

/*
 * Copyright 2017 Google Inc.
 *
 * 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
 *
 *     http://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.
 */

package com.google.firebase.database.core.utilities;

import com.google.firebase.database.core.Path;
import com.google.firebase.database.snapshot.ChildKey;

import java.util.Map;

public class Tree {

  private ChildKey name;
  private Tree parent;
  private TreeNode node;

  public Tree(ChildKey name, Tree parent, TreeNode node) {
    this.name = name;
    this.parent = parent;
    this.node = node;
  }

  public Tree() {
    this(null, null, new TreeNode());
  }

  public TreeNode lastNodeOnPath(Path path) {
    TreeNode current = this.node;
    ChildKey next = path.getFront();
    while (next != null) {
      TreeNode childNode =
          current.children.containsKey(next) ? current.children.get(next) : null;
      if (childNode == null) {
        return current;
      }
      current = childNode;
      path = path.popFront();
      next = path.getFront();
    }
    return current;
  }

  public Tree subTree(Path path) {
    Tree child = this;
    ChildKey next = path.getFront();
    while (next != null) {
      TreeNode childNode =
          child.node.children.containsKey(next) ? child.node.children.get(next) : new TreeNode();
      child = new Tree<>(next, child, childNode);
      path = path.popFront();
      next = path.getFront();
    }
    return child;
  }

  public T getValue() {
    return node.value;
  }

  public void setValue(T value) {
    node.value = value;
    updateParents();
  }

  public Tree getParent() {
    return parent;
  }

  public ChildKey getName() {
    return name;
  }

  public Path getPath() {
    if (parent != null) {
      assert name != null;
      return parent.getPath().child(name);
    } else {
      return (name != null) ? new Path(name) : Path.getEmptyPath();
    }
  }

  public boolean hasChildren() {
    return !node.children.isEmpty();
  }

  public boolean isEmpty() {
    return node.value == null && node.children.isEmpty();
  }

  public void forEachDescendant(TreeVisitor visitor) {
    forEachDescendant(visitor, false, false);
  }

  public void forEachDescendant(TreeVisitor visitor, boolean includeSelf) {
    forEachDescendant(visitor, includeSelf, false);
  }

  public void forEachDescendant(
      final TreeVisitor visitor, boolean includeSelf, final boolean childrenFirst) {
    if (includeSelf && !childrenFirst) {
      visitor.visitTree(this);
    }

    forEachChild(
        new TreeVisitor() {
          @Override
          public void visitTree(Tree tree) {
            tree.forEachDescendant(visitor, true, childrenFirst);
          }
        });

    if (includeSelf && childrenFirst) {
      visitor.visitTree(this);
    }
  }

  public boolean forEachAncestor(TreeFilter filter) {
    return forEachAncestor(filter, false);
  }

  public boolean forEachAncestor(TreeFilter filter, boolean includeSelf) {
    Tree tree = includeSelf ? this : this.parent;
    while (tree != null) {
      if (filter.filterTreeNode(tree)) {
        return true;
      }
      tree = tree.parent;
    }
    return false;
  }

  public void forEachChild(TreeVisitor visitor) {
    // Decouple from actual tree so we can avoid ConcurrentModification exceptions
    Object[] entries = node.children.entrySet().toArray();
    for (int i = 0; i < entries.length; ++i) {
      @SuppressWarnings("unchecked")
      Map.Entry> entry = (Map.Entry>) entries[i];
      Tree subTree = new Tree<>(entry.getKey(), this, entry.getValue());
      visitor.visitTree(subTree);
    }
  }

  private void updateParents() {
    if (parent != null) {
      parent.updateChild(name, this);
    }
  }

  private void updateChild(ChildKey name, Tree child) {
    boolean childEmpty = child.isEmpty();
    boolean childExists = node.children.containsKey(name);
    if (childEmpty && childExists) {
      node.children.remove(name);
      updateParents();
    } else if (!childEmpty && !childExists) {
      node.children.put(name, child.node);
      updateParents();
    }
  }

  @Override
  public String toString() {
    return toString("");
  }

  String toString(String prefix) {
    String nodeName = name == null ? "" : name.asString();
    return prefix + nodeName + "\n" + node.toString(prefix + "\t");
  }

  public interface TreeVisitor {

    void visitTree(Tree tree);
  }

  public interface TreeFilter {

    boolean filterTreeNode(Tree tree);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy