All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.firebase.database.core.utilities.ImmutableTree 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.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);
}
}