/*
* 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.collection;
import java.util.Comparator;
public abstract class LLRBValueNode<K, V> implements LLRBNode<K, V> {
private final K key;
private final V value;
private final LLRBNode<K, V> right;
private LLRBNode<K, V> left;
LLRBValueNode(K key, V value, LLRBNode<K, V> left, LLRBNode<K, V> right) {
this.key = key;
this.value = value;
this.left = left == null ? LLRBEmptyNode.<K, V>getInstance() : left;
this.right = right == null ? LLRBEmptyNode.<K, V>getInstance() : right;
}
private static Color oppositeColor(LLRBNode node) {
return node.isRed() ? Color.BLACK : Color.RED;
}
@Override
public LLRBNode<K, V> getLeft() {
return left;
}
// For use by the builder, which is package local
void setLeft(LLRBNode<K, V> left) {
this.left = left;
}
@Override
public LLRBNode<K, V> getRight() {
return right;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
protected abstract Color getColor();
protected abstract LLRBValueNode<K, V> copy(
K key, V value, LLRBNode<K, V> left, LLRBNode<K, V> right);
@Override
public LLRBValueNode<K, V> copy(
K key, V value, Color color, LLRBNode<K, V> left, LLRBNode<K, V> right) {
K newKey = key == null ? this.key : key;
V newValue = value == null ? this.value : value;
LLRBNode<K, V> newLeft = left == null ? this.left : left;
LLRBNode<K, V> newRight = right == null ? this.right : right;
if (color == Color.RED) {
return new LLRBRedValueNode<>(newKey, newValue, newLeft, newRight);
} else {
return new LLRBBlackValueNode<>(newKey, newValue, newLeft, newRight);
}
}
@Override
public LLRBNode<K, V> insert(K key, V value, Comparator<K> comparator) {
int cmp = comparator.compare(key, this.key);
LLRBValueNode<K, V> n;
if (cmp < 0) {
// new key is less than current key
LLRBNode<K, V> newLeft = this.left.insert(key, value, comparator);
n = copy(null, null, newLeft, null);
} else if (cmp == 0) {
// same key
n = copy(key, value, null, null);
} else {
// new key is greater than current key
LLRBNode<K, V> newRight = this.right.insert(key, value, comparator);
n = copy(null, null, null, newRight);
}
return n.fixUp();
}
@Override
public LLRBNode<K, V> remove(K key, Comparator<K> comparator) {
LLRBValueNode<K, V> n = this;
if (comparator.compare(key, n.key) < 0) {
if (!n.left.isEmpty() && !n.left.isRed() && !((LLRBValueNode<K, V>) n.left).left.isRed()) {
n = n.moveRedLeft();
}
n = n.copy(null, null, n.left.remove(key, comparator), null);
} else {
if (n.left.isRed()) {
n = n.rotateRight();
}
if (!n.right.isEmpty() && !n.right.isRed() && !((LLRBValueNode<K, V>) n.right).left.isRed()) {
n = n.moveRedRight();
}
if (comparator.compare(key, n.key) == 0) {
if (n.right.isEmpty()) {
return LLRBEmptyNode.getInstance();
} else {
LLRBNode<K, V> smallest = n.right.getMin();
n =
n.copy(
smallest.getKey(),
smallest.getValue(),
null,
((LLRBValueNode<K, V>) n.right).removeMin());
}
}
n = n.copy(null, null, null, n.right.remove(key, comparator));
}
return n.fixUp();
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public LLRBNode<K, V> getMin() {
if (left.isEmpty()) {
return this;
} else {
return left.getMin();
}
}
@Override
public LLRBNode<K, V> getMax() {
if (right.isEmpty()) {
return this;
} else {
return right.getMax();
}
}
@Override
public int count() {
return left.count() + 1 + right.count();
}
@Override
public void inOrderTraversal(NodeVisitor<K, V> visitor) {
left.inOrderTraversal(visitor);
visitor.visitEntry(key, value);
right.inOrderTraversal(visitor);
}
@Override
public boolean shortCircuitingInOrderTraversal(ShortCircuitingNodeVisitor<K, V> visitor) {
if (left.shortCircuitingInOrderTraversal(visitor)) {
if (visitor.shouldContinue(key, value)) {
return right.shortCircuitingInOrderTraversal(visitor);
}
}
return false;
}
@Override
public boolean shortCircuitingReverseOrderTraversal(ShortCircuitingNodeVisitor<K, V> visitor) {
if (right.shortCircuitingReverseOrderTraversal(visitor)) {
if (visitor.shouldContinue(key, value)) {
return left.shortCircuitingReverseOrderTraversal(visitor);
}
}
return false;
}
private LLRBNode<K, V> removeMin() {
if (left.isEmpty()) {
return LLRBEmptyNode.getInstance();
} else {
LLRBValueNode<K, V> n = this;
if (!n.getLeft().isRed() && !n.getLeft().getLeft().isRed()) {
n = n.moveRedLeft();
}
n = n.copy(null, null, ((LLRBValueNode<K, V>) n.left).removeMin(), null);
return n.fixUp();
}
}
private LLRBValueNode<K, V> moveRedLeft() {
LLRBValueNode<K, V> n = colorFlip();
if (n.getRight().getLeft().isRed()) {
n = n.copy(null, null, null, ((LLRBValueNode<K, V>) n.getRight()).rotateRight());
n = n.rotateLeft();
n = n.colorFlip();
}
return n;
}
private LLRBValueNode<K, V> moveRedRight() {
LLRBValueNode<K, V> n = colorFlip();
if (n.getLeft().getLeft().isRed()) {
n = n.rotateRight();
n = n.colorFlip();
}
return n;
}
private LLRBValueNode<K, V> fixUp() {
LLRBValueNode<K, V> n = this;
if (n.right.isRed() && !n.left.isRed()) {
n = n.rotateLeft();
}
if (n.left.isRed() && ((LLRBValueNode<K, V>) (n.left)).left.isRed()) {
n = n.rotateRight();
}
if (n.left.isRed() && n.right.isRed()) {
n = n.colorFlip();
}
return n;
}
private LLRBValueNode<K, V> rotateLeft() {
LLRBValueNode<K, V> newLeft =
this.copy(null, null, Color.RED, null, ((LLRBValueNode<K, V>) (this.right)).left);
return (LLRBValueNode<K, V>) this.right.copy(null, null, this.getColor(), newLeft, null);
}
private LLRBValueNode<K, V> rotateRight() {
LLRBValueNode<K, V> newRight =
this.copy(null, null, Color.RED, ((LLRBValueNode<K, V>) (this.left)).right, null);
return (LLRBValueNode<K, V>) this.left.copy(null, null, this.getColor(), null, newRight);
}
private LLRBValueNode<K, V> colorFlip() {
LLRBNode<K, V> newLeft = this.left.copy(null, null, oppositeColor(this.left), null, null);
LLRBNode<K, V> newRight = this.right.copy(null, null, oppositeColor(this.right), null, null);
return this.copy(null, null, oppositeColor(this), newLeft, newRight);
}
}