|
|
@@ -0,0 +1,650 @@ |
|
|
|
/* |
|
|
|
Copyright (c) 2008 Health Market Science, Inc. |
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or |
|
|
|
modify it under the terms of the GNU Lesser General Public |
|
|
|
License as published by the Free Software Foundation; either |
|
|
|
version 2.1 of the License, or (at your option) any later version. |
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful, |
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
|
|
Lesser General Public License for more details. |
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public |
|
|
|
License along with this library; if not, write to the Free Software |
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|
|
|
USA |
|
|
|
|
|
|
|
You can contact Health Market Science at info@healthmarketscience.com |
|
|
|
or at the following address: |
|
|
|
|
|
|
|
Health Market Science |
|
|
|
2700 Horizon Drive |
|
|
|
Suite 200 |
|
|
|
King of Prussia, PA 19406 |
|
|
|
*/ |
|
|
|
|
|
|
|
package com.healthmarketscience.jackcess; |
|
|
|
|
|
|
|
import java.io.IOException; |
|
|
|
import java.lang.ref.Reference; |
|
|
|
import java.lang.ref.SoftReference; |
|
|
|
import java.nio.ByteBuffer; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import org.apache.commons.lang.ObjectUtils; |
|
|
|
|
|
|
|
import static com.healthmarketscience.jackcess.Index.*; |
|
|
|
|
|
|
|
/** |
|
|
|
* Manager of the index pages for a BigIndex. |
|
|
|
* @author James Ahlborn |
|
|
|
*/ |
|
|
|
public class IndexPageCache |
|
|
|
{ |
|
|
|
/** the index whose pages this cache is managing */ |
|
|
|
private final BigIndex _index; |
|
|
|
/** the root page for the index */ |
|
|
|
private DataPageMain _rootPage; |
|
|
|
/** the currently loaded pages for this index, pageNumber -> page */ |
|
|
|
private final Map<Integer, DataPageMain> _dataPages = |
|
|
|
new HashMap<Integer, DataPageMain>(); |
|
|
|
/** the currently modified index pages */ |
|
|
|
private final List<CacheDataPage> _modifiedPages = |
|
|
|
new ArrayList<CacheDataPage>(); |
|
|
|
|
|
|
|
public IndexPageCache(BigIndex index) { |
|
|
|
_index = index; |
|
|
|
} |
|
|
|
|
|
|
|
public BigIndex getIndex() { |
|
|
|
return _index; |
|
|
|
} |
|
|
|
|
|
|
|
public JetFormat getFormat() { |
|
|
|
return getIndex().getFormat(); |
|
|
|
} |
|
|
|
|
|
|
|
public PageChannel getPageChannel() { |
|
|
|
return getIndex().getPageChannel(); |
|
|
|
} |
|
|
|
|
|
|
|
public void setRootPageNumber(int pageNumber) throws IOException { |
|
|
|
_rootPage = getDataPage(pageNumber); |
|
|
|
} |
|
|
|
|
|
|
|
public void write() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
preparePagesForWriting(); |
|
|
|
writeDataPages(); |
|
|
|
} |
|
|
|
|
|
|
|
private void preparePagesForWriting() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
// FIXME, writeme |
|
|
|
} |
|
|
|
|
|
|
|
private void writeDataPages() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
for(CacheDataPage cacheDataPage : _modifiedPages) { |
|
|
|
writeDataPage(cacheDataPage); |
|
|
|
} |
|
|
|
_modifiedPages.clear(); |
|
|
|
} |
|
|
|
|
|
|
|
public CacheDataPage getCacheDataPage(Integer pageNumber) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageMain main = getDataPage(pageNumber); |
|
|
|
return((main != null) ? new CacheDataPage(main) : null); |
|
|
|
} |
|
|
|
|
|
|
|
private DataPageMain getDataPage(Integer pageNumber) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageMain dataPage = _dataPages.get(pageNumber); |
|
|
|
if((dataPage == null) && (pageNumber > INVALID_INDEX_PAGE_NUMBER)) { |
|
|
|
dataPage = readDataPage(pageNumber)._main; |
|
|
|
_dataPages.put(pageNumber, dataPage); |
|
|
|
} |
|
|
|
return dataPage; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Writes the given index page to the file. |
|
|
|
*/ |
|
|
|
private void writeDataPage(CacheDataPage cacheDataPage) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
getIndex().writeDataPage(cacheDataPage); |
|
|
|
|
|
|
|
// lastly, mark the page as no longer modified |
|
|
|
cacheDataPage._extra._modified = false; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Reads the given index page from the file. |
|
|
|
*/ |
|
|
|
private CacheDataPage readDataPage(Integer pageNumber) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageMain dataPage = new DataPageMain(pageNumber); |
|
|
|
DataPageExtra extra = new DataPageExtra(); |
|
|
|
CacheDataPage cacheDataPage = new CacheDataPage(dataPage, extra); |
|
|
|
getIndex().readDataPage(cacheDataPage); |
|
|
|
|
|
|
|
// associate the extra info with the main data page |
|
|
|
dataPage.setExtra(extra, true); |
|
|
|
|
|
|
|
return cacheDataPage; |
|
|
|
} |
|
|
|
|
|
|
|
private void removeEntry(CacheDataPage cacheDataPage, |
|
|
|
int entryIdx) |
|
|
|
{ |
|
|
|
DataPageMain dpMain = cacheDataPage._main; |
|
|
|
DataPageExtra dpExtra = cacheDataPage._extra; |
|
|
|
|
|
|
|
setModified(cacheDataPage); |
|
|
|
|
|
|
|
boolean updateFirst = (entryIdx == 0); |
|
|
|
boolean updateLast = (entryIdx == (dpExtra._entries.size() - 1)); |
|
|
|
|
|
|
|
Entry oldEntry = dpExtra._entries.remove(entryIdx); |
|
|
|
dpExtra._totalEntrySize -= oldEntry.size(); |
|
|
|
|
|
|
|
// note, we don't need to futz with the _entryPrefix because a prefix is |
|
|
|
// always still valid on removal |
|
|
|
|
|
|
|
if(dpExtra._entries.isEmpty()) { |
|
|
|
// this page is dead |
|
|
|
if(dpMain.isRoot()) { |
|
|
|
// clear out this page |
|
|
|
dpMain._firstEntry = null; |
|
|
|
dpMain._lastEntry = null; |
|
|
|
dpExtra._entryPrefix = EMPTY_PREFIX; |
|
|
|
if(dpExtra._totalEntrySize != 0) { |
|
|
|
throw new IllegalStateException("Empty page but size is not 0?"); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// FIXME, remove from parent |
|
|
|
Entry oldParentEntry = oldEntry.asNodeEntry(dpMain._pageNumber); |
|
|
|
// FIXME, update next/prev/childTail links |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if(updateFirst) { |
|
|
|
dpMain._firstEntry = dpExtra.getFirstEntry(); |
|
|
|
Entry oldParentEntry = oldEntry.asNodeEntry(dpMain._pageNumber); |
|
|
|
Entry newParentEntry = |
|
|
|
dpMain._firstEntry.asNodeEntry(dpMain._pageNumber); |
|
|
|
// FIXME, need to update parent |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
if(updateLast) { |
|
|
|
dpMain._lastEntry = dpExtra.getLastEntry(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
private void addEntry(CacheDataPage cacheDataPage, |
|
|
|
int entryIdx, |
|
|
|
Entry newEntry) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
addOrReplaceEntry(cacheDataPage, entryIdx, newEntry, true); |
|
|
|
} |
|
|
|
|
|
|
|
private void replaceEntry(CacheDataPage cacheDataPage, |
|
|
|
int entryIdx, |
|
|
|
Entry newEntry) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
addOrReplaceEntry(cacheDataPage, entryIdx, newEntry, false); |
|
|
|
} |
|
|
|
|
|
|
|
private void addOrReplaceEntry(CacheDataPage cacheDataPage, |
|
|
|
int entryIdx, |
|
|
|
Entry newEntry, |
|
|
|
boolean isAdd) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageMain dpMain = cacheDataPage._main; |
|
|
|
DataPageExtra dpExtra = cacheDataPage._extra; |
|
|
|
|
|
|
|
validateEntryForPage(dpMain, newEntry); |
|
|
|
|
|
|
|
setModified(cacheDataPage); |
|
|
|
|
|
|
|
boolean updateFirst = false; |
|
|
|
boolean updateLast = false; |
|
|
|
|
|
|
|
if(isAdd) { |
|
|
|
updateFirst = (entryIdx == 0); |
|
|
|
updateLast = (entryIdx == dpExtra._entries.size()); |
|
|
|
|
|
|
|
dpExtra._entries.add(entryIdx, newEntry); |
|
|
|
dpExtra._totalEntrySize += newEntry.size(); |
|
|
|
} else { |
|
|
|
updateFirst = (entryIdx == 0); |
|
|
|
updateLast = (entryIdx == (dpExtra._entries.size() - 1)); |
|
|
|
|
|
|
|
Entry oldEntry = dpExtra._entries.set(entryIdx, newEntry); |
|
|
|
dpExtra._totalEntrySize += newEntry.size() - oldEntry.size(); |
|
|
|
} |
|
|
|
|
|
|
|
if(updateFirst) { |
|
|
|
Entry oldFirstEntry = dpMain._firstEntry; |
|
|
|
dpMain._firstEntry = newEntry; |
|
|
|
if(!dpMain.isRoot()) { |
|
|
|
// FIXME, handle null oldFirstEntry |
|
|
|
Entry oldParentEntry = oldFirstEntry.asNodeEntry( |
|
|
|
dpMain._pageNumber); |
|
|
|
Entry newParentEntry = |
|
|
|
dpMain._firstEntry.asNodeEntry(dpMain._pageNumber); |
|
|
|
// FIXME, need to update parent |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
if(updateLast) { |
|
|
|
dpMain._lastEntry = newEntry; |
|
|
|
} |
|
|
|
if(updateFirst || updateLast) { |
|
|
|
// update the prefix |
|
|
|
dpExtra._entryPrefix = findNewPrefix(dpExtra._entryPrefix, newEntry); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void validateEntryForPage(DataPageMain dataPage, Entry entry) { |
|
|
|
if(dataPage._leaf != entry.isLeafEntry()) { |
|
|
|
throw new IllegalStateException( |
|
|
|
"Trying to update page with wrong entry type; pageLeaf " + |
|
|
|
dataPage._leaf + ", entryLeaf " + entry.isLeafEntry()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public CacheDataPage findCacheDataPage(Entry e) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageMain curPage = _rootPage; |
|
|
|
while(true) { |
|
|
|
int pageCmp = curPage.compareToPage(e); |
|
|
|
if(pageCmp < 0) { |
|
|
|
|
|
|
|
// find first leaf |
|
|
|
while(!curPage._leaf) { |
|
|
|
curPage = getDataPage(curPage._firstEntry.getSubPageNumber()); |
|
|
|
} |
|
|
|
return new CacheDataPage(curPage); |
|
|
|
|
|
|
|
} else if(pageCmp > 0) { |
|
|
|
|
|
|
|
if(!curPage._leaf) { |
|
|
|
// we need to handle any "childTail" pages, so we aren't done yet |
|
|
|
DataPageMain childTailPage = curPage.getChildTailPage(); |
|
|
|
if((childTailPage != null) && |
|
|
|
(childTailPage.compareToPage(e) >= 0)) { |
|
|
|
curPage = childTailPage; |
|
|
|
} else { |
|
|
|
curPage = getDataPage(curPage._lastEntry.getSubPageNumber()); |
|
|
|
} |
|
|
|
} else { |
|
|
|
return new CacheDataPage(curPage); |
|
|
|
} |
|
|
|
|
|
|
|
} else if(pageCmp == 0) { |
|
|
|
|
|
|
|
DataPageExtra extra = curPage.getExtra(); |
|
|
|
if(curPage._leaf) { |
|
|
|
return new CacheDataPage(curPage, extra); |
|
|
|
} |
|
|
|
|
|
|
|
// need to descend |
|
|
|
int idx = extra.findEntry(e); |
|
|
|
if(idx < 0) { |
|
|
|
idx = missingIndexToInsertionPoint(idx); |
|
|
|
if((idx == 0) || (idx == extra._entries.size())) { |
|
|
|
// this should never happen, cause we already checked first/last |
|
|
|
// entries in compareToPage |
|
|
|
throw new IllegalStateException("first/last entries incorrect"); |
|
|
|
} |
|
|
|
// the insertion point index is actually the entry after the one we |
|
|
|
// want, so move back one element |
|
|
|
--idx; |
|
|
|
} |
|
|
|
|
|
|
|
Entry nodeEntry = extra._entries.get(idx); |
|
|
|
curPage = getDataPage(nodeEntry.getSubPageNumber()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void setModified(CacheDataPage cacheDataPage) |
|
|
|
{ |
|
|
|
if(!cacheDataPage._extra._modified) { |
|
|
|
_modifiedPages.add(cacheDataPage); |
|
|
|
cacheDataPage._extra._modified = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static byte[] findNewPrefix(byte[] curPrefix, Entry newEntry) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
byte[] newEntryBytes = newEntry.getEntryBytes(); |
|
|
|
if(curPrefix.length > newEntryBytes.length) { |
|
|
|
// the entry bytes may include the page number. need to encode the |
|
|
|
// entire entry |
|
|
|
newEntryBytes = new byte[newEntry.size()]; |
|
|
|
newEntry.write(ByteBuffer.wrap(newEntryBytes), EMPTY_PREFIX); |
|
|
|
} |
|
|
|
|
|
|
|
return findCommonPrefix(curPrefix, newEntryBytes); |
|
|
|
} |
|
|
|
|
|
|
|
private static byte[] findCommonPrefix(Entry e1, Entry e2) |
|
|
|
{ |
|
|
|
return findCommonPrefix(e1.getEntryBytes(), e2.getEntryBytes()); |
|
|
|
} |
|
|
|
|
|
|
|
private static byte[] findCommonPrefix(byte[] b1, byte[] b2) |
|
|
|
{ |
|
|
|
int maxLen = b1.length; |
|
|
|
byte[] prefix = b1; |
|
|
|
if(b1.length > b2.length) { |
|
|
|
maxLen = b2.length; |
|
|
|
prefix = b2; |
|
|
|
} |
|
|
|
|
|
|
|
int len = 0; |
|
|
|
while((len < maxLen) && (b1[len] == b2[len])) { |
|
|
|
++len; |
|
|
|
} |
|
|
|
|
|
|
|
if(len < prefix.length) { |
|
|
|
if(len == 0) { |
|
|
|
return EMPTY_PREFIX; |
|
|
|
} |
|
|
|
|
|
|
|
// need new prefix |
|
|
|
byte[] tmpPrefix = new byte[len]; |
|
|
|
System.arraycopy(prefix, 0, tmpPrefix, 0, len); |
|
|
|
prefix = tmpPrefix; |
|
|
|
} |
|
|
|
|
|
|
|
return prefix; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private class DataPageMain implements Comparable<DataPageMain> |
|
|
|
{ |
|
|
|
public final int _pageNumber; |
|
|
|
public Integer _prevPageNumber; |
|
|
|
public Integer _nextPageNumber; |
|
|
|
public Integer _childTailPageNumber; |
|
|
|
public Integer _parentPageNumber; |
|
|
|
public Entry _firstEntry; |
|
|
|
public Entry _lastEntry; |
|
|
|
public boolean _leaf; |
|
|
|
public boolean _tail; |
|
|
|
private Reference<DataPageExtra> _extra; |
|
|
|
|
|
|
|
private DataPageMain(int pageNumber) { |
|
|
|
_pageNumber = pageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
public IndexPageCache getCache() { |
|
|
|
return IndexPageCache.this; |
|
|
|
} |
|
|
|
|
|
|
|
public boolean isRoot() { |
|
|
|
return(this == _rootPage); |
|
|
|
} |
|
|
|
|
|
|
|
public boolean isTail() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
resolveParent(); |
|
|
|
return _tail; |
|
|
|
} |
|
|
|
|
|
|
|
public DataPageMain getParentPage() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
resolveParent(); |
|
|
|
return IndexPageCache.this.getDataPage(_parentPageNumber); |
|
|
|
} |
|
|
|
|
|
|
|
public DataPageMain getPrevPage() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
return IndexPageCache.this.getDataPage(_prevPageNumber); |
|
|
|
} |
|
|
|
|
|
|
|
public DataPageMain getNextPage() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
return IndexPageCache.this.getDataPage(_nextPageNumber); |
|
|
|
} |
|
|
|
|
|
|
|
public DataPageMain getChildTailPage() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
return IndexPageCache.this.getDataPage(_childTailPageNumber); |
|
|
|
} |
|
|
|
|
|
|
|
public DataPageExtra getExtra() |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
DataPageExtra extra = _extra.get(); |
|
|
|
if(extra == null) { |
|
|
|
extra = readDataPage(_pageNumber)._extra; |
|
|
|
setExtra(extra, false); |
|
|
|
} |
|
|
|
|
|
|
|
return extra; |
|
|
|
} |
|
|
|
|
|
|
|
public void setExtra(DataPageExtra extra, boolean isNew) |
|
|
|
throws IOException |
|
|
|
{ |
|
|
|
|
|
|
|
if(isNew) { |
|
|
|
// save first/last entries |
|
|
|
_firstEntry = extra.getFirstEntry(); |
|
|
|
_lastEntry = extra.getLastEntry(); |
|
|
|
} else { |
|
|
|
// check first/last entries, to be safe |
|
|
|
if(!ObjectUtils.equals(_firstEntry, extra.getFirstEntry()) || |
|
|
|
!ObjectUtils.equals(_lastEntry, extra.getLastEntry())) { |
|
|
|
throw new IOException("Unexpected first or last entry found" + |
|
|
|
"; had " + _firstEntry + ", " + _lastEntry + |
|
|
|
"; found " + extra.getFirstEntry() + ", " + |
|
|
|
extra.getLastEntry()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_extra = new SoftReference<DataPageExtra>(extra); |
|
|
|
} |
|
|
|
|
|
|
|
public int compareToPage(Entry e) |
|
|
|
{ |
|
|
|
return((_firstEntry == null) ? 0 : |
|
|
|
((e.compareTo(_firstEntry) < 0) ? -1 : |
|
|
|
((e.compareTo(_lastEntry) > 0) ? 1 : 0))); |
|
|
|
} |
|
|
|
|
|
|
|
public int compareTo(DataPageMain other) |
|
|
|
{ |
|
|
|
// note, only leaf pages can be meaningfully compared |
|
|
|
if(!_leaf || !other._leaf) { |
|
|
|
throw new IllegalArgumentException("Only leaf pages can be compared"); |
|
|
|
} |
|
|
|
if(this == other) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
// note, if there is more than one leaf page, neither should have null |
|
|
|
// entries |
|
|
|
return _firstEntry.compareTo(other._firstEntry); |
|
|
|
} |
|
|
|
|
|
|
|
private void resolveParent() { |
|
|
|
if((_parentPageNumber == null) && !isRoot()) { |
|
|
|
// FIXME, writeme |
|
|
|
// need to determine _parentPageNumber and _tail |
|
|
|
throw new UnsupportedOperationException(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public String toString() { |
|
|
|
return "DPMain[" + _pageNumber + "] " + _leaf + ", " + _firstEntry + |
|
|
|
", " + _lastEntry + ", " + _extra.get(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static class DataPageExtra |
|
|
|
{ |
|
|
|
/** sorted collection of index entries. this is kept in a list instead of |
|
|
|
a SortedSet because the SortedSet has lame traversal utilities */ |
|
|
|
public List<Entry> _entries; |
|
|
|
public byte[] _entryPrefix; |
|
|
|
public int _totalEntrySize; |
|
|
|
public boolean _modified; |
|
|
|
|
|
|
|
private DataPageExtra() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public int findEntry(Entry e) { |
|
|
|
return Collections.binarySearch(_entries, e); |
|
|
|
} |
|
|
|
|
|
|
|
public Entry getFirstEntry() { |
|
|
|
return (!_entries.isEmpty() ? _entries.get(0) : null); |
|
|
|
} |
|
|
|
|
|
|
|
public Entry getLastEntry() { |
|
|
|
return (!_entries.isEmpty() ? _entries.get(_entries.size() - 1) : null); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public String toString() { |
|
|
|
return "DPExtra: " + _entries; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public static final class CacheDataPage |
|
|
|
extends Index.DataPage |
|
|
|
{ |
|
|
|
public final DataPageMain _main; |
|
|
|
public final DataPageExtra _extra; |
|
|
|
|
|
|
|
private CacheDataPage(DataPageMain dataPage) throws IOException { |
|
|
|
this(dataPage, dataPage.getExtra()); |
|
|
|
} |
|
|
|
|
|
|
|
private CacheDataPage(DataPageMain dataPage, DataPageExtra extra) { |
|
|
|
_main = dataPage; |
|
|
|
_extra = extra; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int getPageNumber() { |
|
|
|
return _main._pageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public boolean isLeaf() { |
|
|
|
return _main._leaf; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setLeaf(boolean isLeaf) { |
|
|
|
_main._leaf = isLeaf; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public int getPrevPageNumber() { |
|
|
|
return _main._prevPageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setPrevPageNumber(int pageNumber) { |
|
|
|
_main._prevPageNumber = pageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int getNextPageNumber() { |
|
|
|
return _main._nextPageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setNextPageNumber(int pageNumber) { |
|
|
|
_main._nextPageNumber = pageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public int getChildTailPageNumber() { |
|
|
|
return _main._childTailPageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setChildTailPageNumber(int pageNumber) { |
|
|
|
_main._childTailPageNumber = pageNumber; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public int getTotalEntrySize() { |
|
|
|
return _extra._totalEntrySize; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setTotalEntrySize(int totalSize) { |
|
|
|
_extra._totalEntrySize = totalSize; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public byte[] getEntryPrefix() { |
|
|
|
return _extra._entryPrefix; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setEntryPrefix(byte[] entryPrefix) { |
|
|
|
_extra._entryPrefix = entryPrefix; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
public List<Entry> getEntries() { |
|
|
|
return _extra._entries; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setEntries(List<Entry> entries) { |
|
|
|
_extra._entries = entries; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void addEntry(int idx, Entry entry) throws IOException { |
|
|
|
_main.getCache().addEntry(this, idx, entry); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void removeEntry(int idx) throws IOException { |
|
|
|
_main.getCache().removeEntry(this, idx); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |