
jetbrains.exodus.tree.TreeCursorMutable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xodus-environment Show documentation
Show all versions of xodus-environment Show documentation
Xodus is pure Java transactional schema-less embedded database
The newest version!
/**
* Copyright 2010 - 2022 JetBrains s.r.o.
*
* 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
*
* https://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 jetbrains.exodus.tree;
import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.ExodusException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Cursor with deleteCurrent support.
* May be used for trees with duplicates or without ones.
*/
public class TreeCursorMutable extends TreeCursor implements ITreeCursorMutable {
protected ITreeMutable tree;
protected boolean wasDelete = false;
@Nullable
protected ByteIterable nextAfterRemovedKey = null;
@Nullable
protected ByteIterable nextAfterRemovedValue = null;
@Nullable
private ByteIterable moveToKey = null;
@Nullable
private ByteIterable moveToValue = null;
public TreeCursorMutable(ITreeMutable tree, TreeTraverser traverser) {
super(traverser);
this.tree = tree;
}
public TreeCursorMutable(ITreeMutable tree, TreeTraverser traverser, boolean alreadyIn) {
super(traverser, alreadyIn);
this.tree = tree;
}
@Override
public boolean getNext() {
moveIfNecessary();
if (wasDelete) {
wasDelete = false;
// move to remembered next
final ByteIterable key = nextAfterRemovedKey;
if (key != null) {
if (traverser.moveTo(key, tree.isAllowingDuplicates() ? nextAfterRemovedValue : null)) {
inited = true;
return true;
}
return false;
} else {
return false;
}
} else {
return super.getNext();
}
}
protected void reset(MutableTreeRoot newRoot) {
traverser.reset(newRoot);
canGoDown = true;
alreadyIn = false;
inited = false;
}
@Override
protected boolean hasNext() {
moveIfNecessary();
return wasDelete ? nextAfterRemovedKey != null : super.hasNext();
}
@Override
public boolean getPrev() {
moveIfNecessary();
if (wasDelete) {
throw new UnsupportedOperationException();
} else {
return super.getPrev();
}
}
@Override
protected boolean hasPrev() {
moveIfNecessary();
if (wasDelete) {
throw new UnsupportedOperationException();
} else {
return super.hasPrev();
}
}
@Override
public @NotNull ByteIterable getKey() {
moveIfNecessary();
return super.getKey();
}
@Override
public @NotNull ByteIterable getValue() {
moveIfNecessary();
return super.getValue();
}
@Override
public boolean deleteCurrent() {
moveIfNecessary();
if (wasDelete) {
return false;
}
// delete and remember next
final ByteIterable key = getKey();
final ByteIterable value = getValue();
if (getNext()) {
nextAfterRemovedKey = traverser.getKey();
nextAfterRemovedValue = traverser.getValue();
} else {
nextAfterRemovedKey = null;
nextAfterRemovedValue = null;
}
// don't call back treeChanged() for current cursor
boolean result = tree.isAllowingDuplicates() ? tree.delete(key, value, this) : tree.delete(key, null, this);
wasDelete = true;
// root may be changed by tree.delete, so reset cursor with new root
reset(tree.getRoot());
return true;
}
@Override
public boolean isMutable() {
return true;
}
@Override
public void treeChanged() {
if (moveToKey == null) {
final ByteIterable key = getKey();
final ByteIterable value = getValue();
moveToKey = key;
moveToValue = value;
}
}
@Override
public void close() {
tree.cursorClosed(this);
}
@Nullable
@Override
protected ByteIterable moveTo(@NotNull ByteIterable key, @Nullable ByteIterable value, boolean rangeSearch) {
final ByteIterable result = super.moveTo(key, value, rangeSearch);
if (result != null) {
wasDelete = false;
}
return result;
}
public static void notifyCursors(ITreeMutable tree) {
final Iterable openCursors = tree.getOpenCursors();
if (openCursors != null) {
for (ITreeCursorMutable cursor : openCursors) {
cursor.treeChanged();
}
}
}
@SuppressWarnings({"ObjectEquality"})
public static void notifyCursors(ITreeMutable tree, ITreeCursorMutable cursorToSkip) {
final Iterable openCursors = tree.getOpenCursors();
if (openCursors != null) {
for (ITreeCursorMutable cursor : openCursors) {
if (cursor != cursorToSkip) {
cursor.treeChanged();
}
}
}
}
protected void moveIfNecessary() {
if (moveToKey != null) {
if (moveToValue == null) {
throw new ExodusException("Can't move Cursor to null value");
}
moveToPair(moveToKey, moveToValue);
moveToKey = moveToValue = null;
}
}
private void moveToPair(@NotNull final ByteIterable key, @NotNull final ByteIterable value) {
reset(tree.getRoot());
// move to current
final boolean withDuplicates = tree.isAllowingDuplicates();
if (!traverser.moveTo(key, withDuplicates ? value : null)) {
wasDelete = true;
// null means current was removed
// try to move to next key/value
if (withDuplicates) {
if (!(traverser.moveToRange(key, value) || traverser.moveToRange(key, null))) {
// null means key/value was removed, move to next key
return;
}
} else if (!traverser.moveToRange(key, null)) {
return;
}
nextAfterRemovedKey = traverser.getKey();
nextAfterRemovedValue = traverser.getValue();
} else {
inited = true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy