blob: e3ae4844690bbcc33fd8a252ed3ebeb0994750ad [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.kaha.impl.index;
import java.io.IOException;
import org.apache.activemq.kaha.StoreEntry;
/**
* A linked list used by IndexItems
*
*
*/
public class DiskIndexLinkedList implements IndexLinkedList {
protected IndexManager indexManager;
protected transient IndexItem root;
protected transient IndexItem last;
protected transient int size;
/**
* Constructs an empty list.
*/
public DiskIndexLinkedList(IndexManager im, IndexItem header) {
this.indexManager = im;
this.root = header;
}
public synchronized IndexItem getRoot() {
return root;
}
public void setRoot(IndexItem e) {
this.root = e;
}
/**
* Returns the first element in this list.
*
* @return the first element in this list.
*/
public synchronized IndexItem getFirst() {
if (size == 0) {
return null;
}
return getNextEntry(root);
}
/**
* Returns the last element in this list.
*
* @return the last element in this list.
*/
public synchronized IndexItem getLast() {
if (size == 0) {
return null;
}
if (last != null) {
last.next = null;
last.setNextItem(IndexItem.POSITION_NOT_SET);
}
return last;
}
/**
* Removes and returns the first element from this list.
*
* @return the first element from this list.
*/
public synchronized StoreEntry removeFirst() {
if (size == 0) {
return null;
}
IndexItem result = getNextEntry(root);
remove(result);
return result;
}
/**
* Removes and returns the last element from this list.
*
* @return the last element from this list.
*/
public synchronized Object removeLast() {
if (size == 0) {
return null;
}
StoreEntry result = last;
remove(last);
return result;
}
/**
* Inserts the given element at the beginning of this list.
*
* @param o the element to be inserted at the beginning of this list.
*/
public synchronized void addFirst(IndexItem item) {
if (size == 0) {
last = item;
}
size++;
}
/**
* Appends the given element to the end of this list. (Identical in function
* to the <tt>add</tt> method; included only for consistency.)
*
* @param o the element to be inserted at the end of this list.
*/
public synchronized void addLast(IndexItem item) {
size++;
last = item;
}
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list.
*/
public synchronized int size() {
return size;
}
/**
* is the list empty?
*
* @return true if there are no elements in the list
*/
public synchronized boolean isEmpty() {
return size == 0;
}
/**
* Appends the specified element to the end of this list.
*
* @param o element to be appended to this list.
* @return <tt>true</tt> (as per the general contract of
* <tt>Collection.add</tt>).
*/
public synchronized boolean add(IndexItem item) {
addLast(item);
return true;
}
/**
* Removes all of the elements from this list.
*/
public synchronized void clear() {
last = null;
size = 0;
}
// Positional Access Operations
/**
* Returns the element at the specified position in this list.
*
* @param index index of element to return.
* @return the element at the specified position in this list.
* @throws IndexOutOfBoundsException if the specified index is is out of
* range (<tt>index &lt; 0 || index &gt;= size()</tt>).
*/
public synchronized IndexItem get(int index) {
return entry(index);
}
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any subsequent
* elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted.
* @param element element to be inserted.
* @throws IndexOutOfBoundsException if the specified index is out of range (<tt>index &lt; 0 || index &gt; size()</tt>).
*/
public synchronized void add(int index, IndexItem element) {
if (index == size) {
last = element;
}
size++;
}
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to removed.
* @return the element previously at the specified position.
* @throws IndexOutOfBoundsException if the specified index is out of range (<tt>index &lt; 0 || index &gt;= size()</tt>).
*/
public synchronized Object remove(int index) {
IndexItem e = entry(index);
remove(e);
return e;
}
/**
* Return the indexed entry.
*/
private IndexItem entry(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
IndexItem e = root;
for (int i = 0; i <= index; i++) {
e = getNextEntry(e);
}
if (e != null && last != null && last.equals(e)) {
last = e;
}
return e;
}
// Search Operations
/**
* Returns the index in this list of the first occurrence of the specified
* element, or -1 if the List does not contain this element. More formally,
* returns the lowest index i such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, or -1 if there
* is no such index.
*
* @param o element to search for.
* @return the index in this list of the first occurrence of the specified
* element, or -1 if the list does not contain this element.
*/
public synchronized int indexOf(StoreEntry o) {
int index = 0;
if (size > 0) {
for (IndexItem e = getNextEntry(root); e != null; e = getNextEntry(e)) {
if (o.equals(e)) {
return index;
}
index++;
}
}
return -1;
}
/**
* Retrieve the next entry after this entry
*
* @param entry
* @return next entry
*/
public synchronized IndexItem getNextEntry(IndexItem current) {
IndexItem result = null;
if (current != null) {
current = (IndexItem) refreshEntry(current);
if (current.getNextItem() >= 0) {
try {
result = indexManager.getIndex(current.getNextItem());
} catch (IOException e) {
throw new RuntimeException("Failed to get next index from "
+ indexManager + " for " + current, e);
}
}
}
// essential last get's updated consistently
if (result != null && last != null && last.equals(result)) {
last=result;
}
return result;
}
/**
* Retrive the prev entry after this entry
*
* @param entry
* @return prev entry
*/
public synchronized IndexItem getPrevEntry(IndexItem current) {
IndexItem result = null;
if (current != null) {
if (current.getPreviousItem() >= 0) {
current = (IndexItem) refreshEntry(current);
try {
result = indexManager.getIndex(current.getPreviousItem());
} catch (IOException e) {
throw new RuntimeException(
"Failed to get current index for " + current, e);
}
}
}
// essential root get's updated consistently
if (result != null && root != null && root.equals(result)) {
return null;
}
return result;
}
public synchronized StoreEntry getEntry(StoreEntry current) {
StoreEntry result = null;
if (current != null && current.getOffset() >= 0) {
try {
result = indexManager.getIndex(current.getOffset());
} catch (IOException e) {
throw new RuntimeException("Failed to index", e);
}
}
// essential root get's updated consistently
if (result != null && root != null && root.equals(result)) {
return root;
}
return result;
}
/**
* Update the indexes of a StoreEntry
*
* @param current
*/
public synchronized StoreEntry refreshEntry(StoreEntry current) {
StoreEntry result = null;
if (current != null && current.getOffset() >= 0) {
try {
result = indexManager.refreshIndex((IndexItem)current);
} catch (IOException e) {
throw new RuntimeException("Failed to index", e);
}
}
// essential root get's updated consistently
if (result != null && root != null && root.equals(result)) {
return root;
}
return result;
}
public synchronized void remove(IndexItem e) {
if (e==null || e == root || e.equals(root)) {
return;
}
if (e == last || e.equals(last)) {
if (size > 1) {
last = (IndexItem)refreshEntry(last);
last = getPrevEntry(last);
} else {
last = null;
}
}
size--;
}
}