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

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

Go to download

This is the official Firebase Admin Java SDK. Build extraordinary native JVM apps in minutes with Firebase. The Firebase platform can power your app’s backend, user authentication, static hosting, and more.

There is a newer version: 9.2.0
Show newest version
/*
 * 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.collection.ImmutableSortedMap;
import com.google.firebase.database.collection.StandardComparator;
import com.google.firebase.database.core.Path;
import com.google.firebase.database.snapshot.ChildKey;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@SuppressWarnings("rawtypes")
public class ImmutableTree implements Iterable> {

  private static final ImmutableSortedMap EMPTY_CHILDREN =
      ImmutableSortedMap.Builder.emptyMap(StandardComparator.getComparator(ChildKey.class));

  @SuppressWarnings("unchecked")
  private static final ImmutableTree EMPTY = new ImmutableTree<>(null, EMPTY_CHILDREN);

  private final T value;
  private final ImmutableSortedMap> children;

  public ImmutableTree(T value, ImmutableSortedMap> children) {
    this.value = value;
    this.children = children;
  }

  @SuppressWarnings("unchecked")
  public ImmutableTree(T value) {
    this(value, EMPTY_CHILDREN);
  }

  @SuppressWarnings("unchecked")
  public static  ImmutableTree emptyInstance() {
    return EMPTY;
  }

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

  public ImmutableSortedMap> getChildren() {
    return this.children;
  }

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

  public Path findRootMostMatchingPath(Path relativePath, Predicate predicate) {
    if (this.value != null && predicate.evaluate(this.value)) {
      return Path.getEmptyPath();
    } else {
      if (relativePath.isEmpty()) {
        return null;
      } else {
        ChildKey front = relativePath.getFront();
        ImmutableTree child = this.children.get(front);
        if (child != null) {
          Path path = child.findRootMostMatchingPath(relativePath.popFront(), predicate);
          if (path != null) {
            // TODO: this seems inefficient
            return new Path(front).child(path);
          } else {
            return null;
          }
        } else {
          return null;
        }
      }
    }
  }

  public Path findRootMostPathWithValue(Path relativePath) {
    return findRootMostMatchingPath(relativePath, Predicate.TRUE);
  }

  public T rootMostValue(Path relativePath) {
    return rootMostValueMatching(relativePath, Predicate.TRUE);
  }

  public T rootMostValueMatching(Path relativePath, Predicate predicate) {
    if (this.value != null && predicate.evaluate(this.value)) {
      return this.value;
    } else {
      ImmutableTree currentTree = this;
      for (ChildKey key : relativePath) {
        currentTree = currentTree.children.get(key);
        if (currentTree == null) {
          return null;
        } else if (currentTree.value != null && predicate.evaluate(currentTree.value)) {
          return currentTree.value;
        }
      }
      return null;
    }
  }

  public T leafMostValue(Path relativePath) {
    return leafMostValueMatching(relativePath, Predicate.TRUE);
  }

  /**
   * Returns the deepest value found between the root and the specified path that matches the
   * predicate.
   *
   * @param path Path along which to look for matching values.
   * @param predicate The predicate to evaluate values against.
   * @return The deepest matching value, or null if no value matches.
   */
  public T leafMostValueMatching(Path path, Predicate predicate) {
    T currentValue = (this.value != null && predicate.evaluate(this.value)) ? this.value : null;
    ImmutableTree currentTree = this;
    for (ChildKey key : path) {
      currentTree = currentTree.children.get(key);
      if (currentTree == null) {
        return currentValue;
      } else {
        if (currentTree.value != null && predicate.evaluate(currentTree.value)) {
          currentValue = currentTree.value;
        }
      }
    }
    return currentValue;
  }

  public boolean containsMatchingValue(Predicate predicate) {
    if (this.value != null && predicate.evaluate(this.value)) {
      return true;
    } else {
      for (Map.Entry> subtree : this.children) {
        if (subtree.getValue().containsMatchingValue(predicate)) {
          return true;
        }
      }
      return false;
    }
  }

  public ImmutableTree getChild(ChildKey child) {
    ImmutableTree childTree = this.children.get(child);
    if (childTree != null) {
      return childTree;
    } else {
      return emptyInstance();
    }
  }

  public ImmutableTree subtree(Path relativePath) {
    if (relativePath.isEmpty()) {
      return this;
    } else {
      ChildKey front = relativePath.getFront();
      ImmutableTree childTree = this.children.get(front);
      if (childTree != null) {
        return childTree.subtree(relativePath.popFront());
      } else {
        return emptyInstance();
      }
    }
  }

  public ImmutableTree set(Path relativePath, T value) {
    if (relativePath.isEmpty()) {
      return new ImmutableTree<>(value, this.children);
    } else {
      ChildKey front = relativePath.getFront();
      ImmutableTree child = this.children.get(front);
      if (child == null) {
        child = emptyInstance();
      }
      ImmutableTree newChild = child.set(relativePath.popFront(), value);
      ImmutableSortedMap> newChildren =
          this.children.insert(front, newChild);
      return new ImmutableTree<>(this.value, newChildren);
    }
  }

  public ImmutableTree remove(Path relativePath) {
    if (relativePath.isEmpty()) {
      if (this.children.isEmpty()) {
        return emptyInstance();
      } else {
        return new ImmutableTree<>(null, this.children);
      }
    } else {
      ChildKey front = relativePath.getFront();
      ImmutableTree child = this.children.get(front);
      if (child != null) {
        ImmutableTree newChild = child.remove(relativePath.popFront());
        ImmutableSortedMap> newChildren;
        if (newChild.isEmpty()) {
          newChildren = this.children.remove(front);
        } else {
          newChildren = this.children.insert(front, newChild);
        }
        if (this.value == null && newChildren.isEmpty()) {
          return emptyInstance();
        } else {
          return new ImmutableTree<>(this.value, newChildren);
        }
      } else {
        return this;
      }
    }
  }

  public T get(Path relativePath) {
    if (relativePath.isEmpty()) {
      return this.value;
    } else {
      ChildKey front = relativePath.getFront();
      ImmutableTree child = this.children.get(front);
      if (child != null) {
        return child.get(relativePath.popFront());
      } else {
        return null;
      }
    }
  }

  public ImmutableTree setTree(Path relativePath, ImmutableTree newTree) {
    if (relativePath.isEmpty()) {
      return newTree;
    } else {
      ChildKey front = relativePath.getFront();
      ImmutableTree child = this.children.get(front);
      if (child == null) {
        child = emptyInstance();
      }
      ImmutableTree newChild = child.setTree(relativePath.popFront(), newTree);
      ImmutableSortedMap> newChildren;
      if (newChild.isEmpty()) {
        newChildren = this.children.remove(front);
      } else {
        newChildren = this.children.insert(front, newChild);
      }
      return new ImmutableTree<>(this.value, newChildren);
    }
  }

  public void foreach(TreeVisitor visitor) {
    fold(Path.getEmptyPath(), visitor, null);
  }

  public  R fold(R accum, TreeVisitor visitor) {
    return fold(Path.getEmptyPath(), visitor, accum);
  }

  private  R fold(Path relativePath, TreeVisitor visitor, R accum) {
    for (Map.Entry> subtree : this.children) {
      accum = subtree.getValue().fold(relativePath.child(subtree.getKey()), visitor, accum);
    }
    if (this.value != null) {
      accum = visitor.onNodeValue(relativePath, this.value, accum);
    }
    return accum;
  }

  public Collection values() {
    final ArrayList list = new ArrayList<>();
    this.foreach(
        new TreeVisitor() {
          @Override
          public Void onNodeValue(Path relativePath, T value, Void accum) {
            list.add(value);
            return null;
          }
        });
    return list;
  }

  @Override
  public Iterator> iterator() {
    // This could probably be done more efficient than prefilling a list, however, it's also
    // a bit
    // tricky as we have to potentially scan all subtrees for a value that exists. Since
    // iterators
    // are consumed fully in most cases, this should give a fairly efficient implementation
    // in most
    // cases.
    final List> list = new ArrayList<>();
    this.foreach(
        new TreeVisitor() {
          @Override
          public Void onNodeValue(Path relativePath, T value, Void accum) {
            list.add(new AbstractMap.SimpleImmutableEntry<>(relativePath, value));
            return null;
          }
        });
    return list.iterator();
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("ImmutableTree { value=");
    builder.append(getValue());
    builder.append(", children={");
    for (Map.Entry> child : children) {
      builder.append(child.getKey().asString());
      builder.append("=");
      builder.append(child.getValue());
    }
    builder.append("} }");
    return builder.toString();
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    ImmutableTree that = (ImmutableTree) o;

    if (children != null ? !children.equals(that.children) : that.children != null) {
      return false;
    }
    if (value != null ? !value.equals(that.value) : that.value != null) {
      return false;
    }

    return true;
  }

  @Override
  public int hashCode() {
    int result = value != null ? value.hashCode() : 0;
    result = 31 * result + (children != null ? children.hashCode() : 0);
    return result;
  }

  public interface TreeVisitor {

    R onNodeValue(Path relativePath, T value, R accum);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy