SCViewer/com.minres.scviewer.databas.../src/org/apache/jdbm/LinkedList2.java

481 lines
15 KiB
Java

/*
* 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.jdbm;
import java.io.*;
import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* LinkedList2 which stores its nodes on disk.
*
* @author Jan Kotek
*/
class LinkedList2<E> extends AbstractSequentialList<E> {
private DBAbstract db;
final long rootRecid;
/** size limit, is not currently used, but needs to be here for future compatibility.
* Zero means no limit.
*/
long sizeLimit = 0;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static final class Root{
long first;
long last;
long size;
}
private static final Serializer<Root> ROOT_SERIALIZER= new Serializer<Root>(){
public void serialize(DataOutput out, Root obj) throws IOException {
LongPacker.packLong(out,obj.first);
LongPacker.packLong(out,obj.last);
LongPacker.packLong(out,obj.size);
}
public Root deserialize(DataInput in) throws IOException, ClassNotFoundException {
Root r = new Root();
r.first = LongPacker.unpackLong(in);
r.last = LongPacker.unpackLong(in);
r.size = LongPacker.unpackLong(in);
return r;
}
};
private Serializer<E> valueSerializer;
/**
* indicates that entry values should not be loaded during deserialization, used during defragmentation
*/
protected boolean loadValues = true;
/** constructor used for deserialization */
LinkedList2(DBAbstract db,long rootRecid, Serializer<E> valueSerializer) {
this.db = db;
this.rootRecid = rootRecid;
this.valueSerializer = valueSerializer;
}
/** constructor used to create new empty list*/
LinkedList2(DBAbstract db, Serializer<E> valueSerializer) throws IOException {
this.db = db;
if (valueSerializer != null && !(valueSerializer instanceof Serializable))
throw new IllegalArgumentException("Serializer does not implement Serializable");
this.valueSerializer = valueSerializer;
//create root
this.rootRecid = db.insert(new Root(), ROOT_SERIALIZER,false);
}
void setPersistenceContext(DBAbstract db) {
this.db = db;
}
public ListIterator<E> listIterator(int index) {
lock.readLock().lock();
try{
Root r = getRoot();
if (index < 0 || index > r.size)
throw new IndexOutOfBoundsException();
Iter iter = new Iter();
iter.next = r.first;
//scroll to requested position
//TODO scroll from end, if beyond half
for (int i = 0; i < index; i++) {
iter.next();
}
return iter;
}finally {
lock.readLock().unlock();
}
}
Root getRoot(){
//expect that caller already holds lock
try {
return db.fetch(rootRecid,ROOT_SERIALIZER);
} catch (IOException e) {
throw new IOError(e);
}
}
public int size() {
lock.readLock().lock();
try{
return (int) getRoot().size;
}finally {
lock.readLock().unlock();
}
}
public Iterator<E> descendingIterator() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public boolean add(Object value) {
lock.writeLock().lock();
try {
Root r = getRoot();
Entry e = new Entry(r.last, 0, value);
long recid = db.insert(e, entrySerializer,false);
//update old last Entry to point to new record
if (r.last != 0) {
Entry oldLast = db.fetch(r.last, entrySerializer);
if (oldLast.next != 0) throw new Error();
oldLast.next = recid;
db.update(r.last, oldLast, entrySerializer);
}
//update linked list
r.last = recid;
if (r.first == 0) r.first = recid;
r.size++;
db.update(rootRecid, r, ROOT_SERIALIZER);
modCount++;
return true;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
private Entry<E> fetch(long recid) {
lock.readLock().lock();
try {
return db.fetch(recid, entrySerializer);
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.readLock().unlock();
}
}
/**
* called from Serialization object
*/
static LinkedList2 deserialize(DataInput is, Serialization ser) throws IOException, ClassNotFoundException {
long rootrecid = LongPacker.unpackLong(is);
long sizeLimit = LongPacker.unpackLong(is);
if(sizeLimit!=0) throw new InternalError("LinkedList.sizeLimit not supported in this JDBM version");
Serializer serializer = (Serializer) ser.deserialize(is);
return new LinkedList2(ser.db,rootrecid, serializer);
}
void serialize(DataOutput out) throws IOException {
LongPacker.packLong(out, rootRecid);
LongPacker.packLong(out, sizeLimit);
db.defaultSerializer().serialize(out, valueSerializer);
}
private final Serializer<Entry> entrySerializer = new Serializer<Entry>() {
public void serialize(DataOutput out, Entry e) throws IOException {
LongPacker.packLong(out, e.prev);
LongPacker.packLong(out, e.next);
if (valueSerializer != null)
valueSerializer.serialize(out, (E) e.value);
else
db.defaultSerializer().serialize(out, e.value);
}
public Entry<E> deserialize(DataInput in) throws IOException, ClassNotFoundException {
long prev = LongPacker.unpackLong(in);
long next = LongPacker.unpackLong(in);
Object value = null;
if (loadValues)
value = valueSerializer == null ? db.defaultSerializer().deserialize(in) : valueSerializer.deserialize(in);
return new LinkedList2.Entry(prev, next, value);
}
};
static class Entry<E> {
long prev = 0;
long next = 0;
E value;
public Entry(long prev, long next, E value) {
this.prev = prev;
this.next = next;
this.value = value;
}
}
private final class Iter implements ListIterator<E> {
private int expectedModCount = modCount;
private int index = 0;
private long prev = 0;
private long next = 0;
private byte lastOper = 0;
public boolean hasNext() {
return next != 0;
}
public E next() {
if (next == 0) throw new NoSuchElementException();
checkForComodification();
Entry<E> e = fetch(next);
prev = next;
next = e.next;
index++;
lastOper = +1;
return e.value;
}
public boolean hasPrevious() {
return prev != 0;
}
public E previous() {
checkForComodification();
Entry<E> e = fetch(prev);
next = prev;
prev = e.prev;
index--;
lastOper = -1;
return e.value;
}
public int nextIndex() {
return index;
}
public int previousIndex() {
return index - 1;
}
public void remove() {
checkForComodification();
lock.writeLock().lock();
try {
if (lastOper == 1) {
//last operation was next() so remove previous element
lastOper = 0;
Entry<E> p = db.fetch(prev, entrySerializer);
//update entry before previous
if (p.prev != 0) {
Entry<E> pp = db.fetch(p.prev, entrySerializer);
pp.next = p.next;
db.update(p.prev, pp, entrySerializer);
}
//update entry after next
if (p.next != 0) {
Entry<E> pn = db.fetch(p.next, entrySerializer);
pn.prev = p.prev;
db.update(p.next, pn, entrySerializer);
}
//remove old record from db
db.delete(prev);
//update list
Root r = getRoot();
if (r.first == prev)
r.first = next;
if (r.last == prev)
r.last = next;
r.size--;
db.update(rootRecid, r,ROOT_SERIALIZER);
modCount++;
expectedModCount++;
//update iterator
prev = p.prev;
} else if (lastOper == -1) {
//last operation was prev() so remove next element
lastOper = 0;
Entry<E> n = db.fetch(next, entrySerializer);
//update entry before next
if (n.prev != 0) {
Entry<E> pp = db.fetch(n.prev, entrySerializer);
pp.next = n.next;
db.update(n.prev, pp, entrySerializer);
}
//update entry after previous
if (n.next != 0) {
Entry<E> pn = db.fetch(n.next, entrySerializer);
pn.prev = n.prev;
db.update(n.next, pn, entrySerializer);
}
//remove old record from db
db.delete(next);
//update list
Root r = getRoot();
if (r.last == next)
r.last = prev;
if (r.first == next)
r.first = prev;
r.size--;
db.update(rootRecid, r,ROOT_SERIALIZER);
modCount++;
expectedModCount++;
//update iterator
next = n.next;
} else
throw new IllegalStateException();
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public void set(E value) {
checkForComodification();
lock.writeLock().lock();
try {
if (lastOper == 1) {
//last operation was next(), so update previous item
lastOper = 0;
Entry<E> n = db.fetch(prev, entrySerializer);
n.value = value;
db.update(prev, n, entrySerializer);
} else if (lastOper == -1) {
//last operation was prev() so update next item
lastOper = 0;
Entry<E> n = db.fetch(next, entrySerializer);
n.value = value;
db.update(next, n, entrySerializer);
} else
throw new IllegalStateException();
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
public void add(E value) {
checkForComodification();
//use more efficient method if possible
if (next == 0) {
LinkedList2.this.add(value);
expectedModCount++;
return;
}
lock.writeLock().lock();
try {
//insert new entry
Entry<E> e = new Entry<E>(prev, next, value);
long recid = db.insert(e, entrySerializer,false);
//update previous entry
if (prev != 0) {
Entry<E> p = db.fetch(prev, entrySerializer);
if (p.next != next) throw new Error();
p.next = recid;
db.update(prev, p, entrySerializer);
}
//update next entry
Entry<E> n = fetch(next);
if (n.prev != prev) throw new Error();
n.prev = recid;
db.update(next, n, entrySerializer);
//update List
Root r = getRoot();
r.size++;
db.update(rootRecid, r, ROOT_SERIALIZER);
//update iterator
expectedModCount++;
modCount++;
prev = recid;
} catch (IOException e) {
throw new IOError(e);
}finally {
lock.writeLock().unlock();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* Copyes collection from one db to other, while keeping logical recids unchanged
*/
static void defrag(long recid, DBStore r1, DBStore r2) throws IOException {
try {
//move linked list itself
byte[] data = r1.fetchRaw(recid);
r2.forceInsert(recid, data);
DataInputOutput in = new DataInputOutput();
in.reset(data);
LinkedList2 l = (LinkedList2) r1.defaultSerializer().deserialize(in);
l.loadValues = false;
//move linkedlist root
if(l.rootRecid == 0) //empty list, done
return;
data = r1.fetchRaw(l.rootRecid);
r2.forceInsert(l.rootRecid, data);
in.reset(data);
Root r = ROOT_SERIALIZER.deserialize(in);
//move all other nodes in linked list
long current = r.first;
while (current != 0) {
data = r1.fetchRaw(current);
in.reset(data);
r2.forceInsert(current, data);
Entry e = (Entry) l.entrySerializer.deserialize(in);
current = e.next;
}
} catch (ClassNotFoundException e) {
throw new IOError(e);
}
}
}