📄 rangecursor.java
字号:
/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Oracle Corporation. All rights reserved. * * $Id: RangeCursor.java,v 1.3 2006/09/08 20:32:14 bostic Exp $ */package com.sleepycat.util.keyrange;import com.sleepycat.compat.DbCompat;import com.sleepycat.db.Cursor;import com.sleepycat.db.DatabaseEntry;import com.sleepycat.db.DatabaseException;import com.sleepycat.db.LockMode;import com.sleepycat.db.OperationStatus;import com.sleepycat.db.SecondaryCursor;/** * A cursor-like interface that enforces a key range. The method signatures * are actually those of SecondaryCursor, but the pKey parameter may be null. * It was done this way to avoid doubling the number of methods. * * <p>This is not a fully general implementation of a range cursor and should * not be used directly by applications; however, it may evolve into a * generally useful range cursor some day.</p> * * @author Mark Hayes */public class RangeCursor implements Cloneable { /** * The cursor and secondary cursor are the same object. The secCursor is * null if the database is not a secondary database. */ private Cursor cursor; private SecondaryCursor secCursor; /** * The range is always non-null, but may be unbounded meaning that it is * open and not used. */ private KeyRange range; /** * The pkRange may be non-null only if the range is a single-key range * and the cursor is a secondary cursor. It further restricts the range of * primary keys in a secondary database. */ private KeyRange pkRange; /** * The privXxx entries are used only when the range is bounded. We read * into these private entries to avoid modifying the caller's entry * parameters in the case where we read successfully but the key is out of * range. In that case we return NOTFOUND and we want to leave the entry * parameters unchanged. */ private DatabaseEntry privKey; private DatabaseEntry privPKey; private DatabaseEntry privData; /** * The initialized flag is set to true whenever we successfully position * the cursor. It is used to implement the getNext/Prev logic for doing a * getFirst/Last when the cursor is not initialized. We can't rely on * Cursor to do that for us, since if we position the underlying cursor * successfully but the key is out of range, we have no way to set the * underlying cursor to uninitialized. A range cursor always starts in the * uninitialized state. */ private boolean initialized; /** * Creates a range cursor. */ public RangeCursor(KeyRange range, Cursor cursor) throws DatabaseException { this.range = range; this.cursor = cursor; init(); } /** * Creates a range cursor with a duplicate range. */ public RangeCursor(KeyRange range, KeyRange pkRange, Cursor cursor) throws DatabaseException { if (pkRange != null && !range.singleKey) { throw new IllegalArgumentException(); } this.range = range; this.pkRange = pkRange; this.cursor = cursor; init(); if (pkRange != null && secCursor == null) { throw new IllegalArgumentException(); } } /** * Create a cloned range cursor. The caller must clone the underlying * cursor before using this constructor, because cursor open/close is * handled specially for CDS cursors outside this class. */ public RangeCursor dup(boolean samePosition) throws DatabaseException { try { RangeCursor c = (RangeCursor) super.clone(); c.cursor = dupCursor(cursor, samePosition); c.init(); return c; } catch (CloneNotSupportedException neverHappens) { return null; } } /** * Used for opening and duping (cloning). */ private void init() { if (cursor instanceof SecondaryCursor) { secCursor = (SecondaryCursor) cursor; } else { secCursor = null; } if (range.hasBound()) { privKey = new DatabaseEntry(); privPKey = new DatabaseEntry(); privData = new DatabaseEntry(); } else { privKey = null; privPKey = null; privData = null; } } /** * Returns whether the cursor is initialized at a valid position. */ public boolean isInitialized() { return initialized; } /** * Returns the underlying cursor. Used for cloning. */ public Cursor getCursor() { return cursor; } /** * When an unbounded range is used, this method is called to use the * callers entry parameters directly, to avoid the extra step of copying * between the private entries and the caller's entries. */ private void setParams(DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data) { privKey = key; privPKey = pKey; privData = data; } /** * Dups the cursor, sets the cursor and secCursor fields to the duped * cursor, and returns the old cursor. Always call endOperation in a * finally clause after calling beginOperation. * * <p>If the returned cursor == the cursor field, the cursor is * uninitialized and was not duped; this case is handled correctly by * endOperation.</p> */ private Cursor beginOperation() throws DatabaseException { Cursor oldCursor = cursor; if (initialized) { cursor = dupCursor(cursor, true); if (secCursor != null) { secCursor = (SecondaryCursor) cursor; } } else { return cursor; } return oldCursor; } /** * If the operation succeded, leaves the duped cursor in place and closes * the oldCursor. If the operation failed, moves the oldCursor back in * place and closes the duped cursor. oldCursor may be null if * beginOperation was not called, in cases where we don't need to dup * the cursor. Always call endOperation when a successful operation ends, * in order to set the initialized field. */ private void endOperation(Cursor oldCursor, OperationStatus status, DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data) throws DatabaseException { if (status == OperationStatus.SUCCESS) { if (oldCursor != null && oldCursor != cursor) { closeCursor(oldCursor); } if (key != null) { swapData(key, privKey); } if (pKey != null && secCursor != null) { swapData(pKey, privPKey); } if (data != null) { swapData(data, privData); } initialized = true; } else { if (oldCursor != null && oldCursor != cursor) { closeCursor(cursor); cursor = oldCursor; if (secCursor != null) { secCursor = (SecondaryCursor) cursor; } } } } /** * Swaps the contents of the two entries. Used to return entry data to * the caller when the operation was successful. */ private static void swapData(DatabaseEntry e1, DatabaseEntry e2) { byte[] d1 = e1.getData(); int o1 = e1.getOffset(); int s1 = e1.getSize(); e1.setData(e2.getData(), e2.getOffset(), e2.getSize()); e2.setData(d1, o1, s1); } /** * Shares the same byte array, offset and size between two entries. * Used when copying the entry data is not necessary because it is known * that the underlying operation will not modify the entry, for example, * with getSearchKey. */ private static void shareData(DatabaseEntry from, DatabaseEntry to) { if (from != null) { to.setData(from.getData(), from.getOffset(), from.getSize()); } } public OperationStatus getFirst(DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode) throws DatabaseException { OperationStatus status; if (!range.hasBound()) { setParams(key, pKey, data); status = doGetFirst(lockMode); endOperation(null, status, null, null, null); return status; } if (pkRange != null) { KeyRange.copy(range.beginKey, privKey); if (pkRange.singleKey) { KeyRange.copy(pkRange.beginKey, privPKey); status = doGetSearchBoth(lockMode); endOperation(null, status, key, pKey, data); } else { status = OperationStatus.NOTFOUND; Cursor oldCursor = beginOperation(); try { if (pkRange.beginKey == null) { status = doGetSearchKey(lockMode); } else { KeyRange.copy(pkRange.beginKey, privPKey); status = doGetSearchBothRange(lockMode); if (status == OperationStatus.SUCCESS && !pkRange.beginInclusive && pkRange.compare(privPKey, pkRange.beginKey) == 0) { status = doGetNextDup(lockMode); } } if (status == OperationStatus.SUCCESS && !pkRange.check(privPKey)) { status = OperationStatus.NOTFOUND; } } finally { endOperation(oldCursor, status, key, pKey, data); } } } else if (range.singleKey) { KeyRange.copy(range.beginKey, privKey); status = doGetSearchKey(lockMode); endOperation(null, status, key, pKey, data); } else { status = OperationStatus.NOTFOUND; Cursor oldCursor = beginOperation(); try { if (range.beginKey == null) { status = doGetFirst(lockMode); } else { KeyRange.copy(range.beginKey, privKey); status = doGetSearchKeyRange(lockMode); if (status == OperationStatus.SUCCESS && !range.beginInclusive && range.compare(privKey, range.beginKey) == 0) { status = doGetNextNoDup(lockMode); } } if (status == OperationStatus.SUCCESS && !range.check(privKey)) { status = OperationStatus.NOTFOUND; } } finally { endOperation(oldCursor, status, key, pKey, data); } } return status; } public OperationStatus getLast(DatabaseEntry key, DatabaseEntry pKey, DatabaseEntry data, LockMode lockMode) throws DatabaseException { OperationStatus status = OperationStatus.NOTFOUND; if (!range.hasBound()) { setParams(key, pKey, data); status = doGetLast(lockMode); endOperation(null, status, null, null, null); return status; } Cursor oldCursor = beginOperation(); try { if (pkRange != null) { KeyRange.copy(range.beginKey, privKey); boolean doLast = false; if (pkRange.endKey == null) { doLast = true; } else { KeyRange.copy(pkRange.endKey, privPKey); status = doGetSearchBothRange(lockMode); if (status == OperationStatus.SUCCESS) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -