2005-04-07 21:19:42 +02:00
// kelondroTree.java
// -----------------------
// part of The Kelondro Database
// (C) by Michael Peter Christen; mc@anomic.de
// first published on http://www.anomic.de
// Frankfurt, Germany, 2004
// last major change: 07.02.2005
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Using this software in any meaning (reading, learning, copying, compiling,
// running) means that you agree that the Author(s) is (are) not responsible
// for cost, loss of data or any harm that may be caused directly or indirectly
// by usage of this softare or this documentation. The usage of this software
// is on your own risk. The installation and usage (starting/running) of this
// software may allow other people or application to access your computer and
// any attached devices and is highly dependent on the configuration of the
// software which must be done by the user of the software; the author(s) is
// (are) also not responsible for proper configuration and usage of the
// software, even if provoked by documentation provided together with
// the software.
//
// Any changes to this file according to the GPL as documented in the file
// gpl.txt aside this file in the shipment you received can be done to the
// lines that follows this copyright notice here, but changes must not be
// done inside the copyright notive above. A re-distribution must contain
// the intact and unchanged copyright notice.
// Contributions and changes to the program code must be marked as such.
/ *
This class extends the kelondroRecords and adds a tree structure
* /
package de.anomic.kelondro ;
2005-05-05 07:32:19 +02:00
import java.io.BufferedReader ;
import java.io.File ;
import java.io.FileReader ;
import java.io.IOException ;
import java.io.RandomAccessFile ;
2008-01-20 02:22:46 +01:00
import java.util.ArrayList ;
2008-01-11 01:12:01 +01:00
import java.util.Comparator ;
2006-10-13 01:14:41 +02:00
import java.util.Date ;
2005-05-05 07:32:19 +02:00
import java.util.HashSet ;
import java.util.Iterator ;
import java.util.LinkedList ;
2007-02-25 22:06:26 +01:00
import java.util.List ;
2006-09-30 00:27:20 +02:00
import java.util.Map ;
2005-05-05 07:32:19 +02:00
import java.util.StringTokenizer ;
2006-03-18 00:43:24 +01:00
import java.util.TreeMap ;
2006-01-24 14:12:38 +01:00
import java.util.TreeSet ;
2005-05-05 07:32:19 +02:00
import java.util.Vector ;
2006-01-31 03:01:32 +01:00
import java.util.logging.Logger ;
2005-04-07 21:19:42 +02:00
2007-02-25 22:06:26 +01:00
import de.anomic.kelondro.kelondroRow.Entry ;
2006-10-24 15:48:16 +02:00
import de.anomic.server.logging.serverLog ;
2007-08-03 13:44:58 +02:00
public class kelondroTree extends kelondroCachedRecords implements kelondroIndex {
2006-01-29 17:04:20 +01:00
2006-01-31 03:01:32 +01:00
// logging (This probably needs someone to initialize the java.util.logging.* facilities);
2006-08-16 21:49:31 +02:00
public static final Logger log = Logger . getLogger ( " KELONDRO " ) ;
2005-04-07 21:19:42 +02:00
// define the Over-Head-Array
2006-08-16 21:49:31 +02:00
protected static final short thisOHBytes = 2 ; // our record definition of two bytes
protected static final short thisOHHandles = 3 ; // and three handles overhead
protected static final short thisFHandles = 1 ; // file handles: one for a root pointer
2005-04-07 21:19:42 +02:00
// define pointers for OH array access
2006-08-16 21:49:31 +02:00
protected static final int magic = 0 ; // pointer for OHByte-array: marker for Node purpose; defaults to 1
protected static final int balance = 1 ; // pointer for OHByte-array: balance value of tree node; balanced = 0
2005-04-07 21:19:42 +02:00
2006-08-16 21:49:31 +02:00
protected static final int parent = 0 ; // pointer for OHHandle-array: handle()-Value of parent Node
protected static final int leftchild = 1 ; // pointer for OHHandle-array: handle()-Value of left child Node
protected static final int rightchild = 2 ; // pointer for OHHandle-array: handle()-Value of right child Node
2005-04-07 21:19:42 +02:00
2006-08-16 21:49:31 +02:00
protected static final int root = 0 ; // pointer for FHandles-array: pointer to root node
2006-05-08 15:37:27 +02:00
// class variables
2008-01-11 01:12:01 +01:00
private final Search writeSearchObj = new Search ( ) ;
protected Comparator < String > loopDetectionOrder ;
protected int readAheadChunkSize = 100 ;
protected long lastIteratorCount = readAheadChunkSize ;
2007-03-06 23:43:32 +01:00
public kelondroTree ( File file , boolean useNodeCache , long preloadTime , kelondroRow rowdef ) throws IOException {
this ( file , useNodeCache , preloadTime , rowdef , rowdef . columns ( ) /* txtProps */ , 80 /* txtPropWidth */ ) ;
2005-04-07 21:19:42 +02:00
}
2007-03-06 23:43:32 +01:00
public kelondroTree ( File file , boolean useNodeCache , long preloadTime , kelondroRow rowdef , int txtProps , int txtPropsWidth ) throws IOException {
2006-08-24 04:19:25 +02:00
// opens an existing tree file or creates a new tree file
2007-03-06 23:43:32 +01:00
super ( file , useNodeCache , preloadTime ,
2006-06-02 14:45:57 +02:00
thisOHBytes , thisOHHandles , rowdef ,
2006-08-24 04:19:25 +02:00
thisFHandles , txtProps , txtPropsWidth ) ;
2006-12-06 04:02:57 +01:00
if ( ! fileExisted ) {
2006-08-24 04:19:25 +02:00
// create new file structure
2005-12-13 00:59:58 +01:00
setHandle ( root , null ) ; // define the root value
}
2006-12-06 04:02:57 +01:00
super . setLogger ( log ) ;
2008-01-11 01:12:01 +01:00
this . loopDetectionOrder = new kelondroByteOrder . StringOrder ( rowdef . objectOrder ) ;
2005-04-07 21:19:42 +02:00
}
2007-03-06 23:43:32 +01:00
public static final kelondroTree open ( File file , boolean useNodeCache , long preloadTime , kelondroRow rowdef ) {
return open ( file , useNodeCache , preloadTime , rowdef , rowdef . columns ( ) /* txtProps */ , 80 /* txtPropWidth */ ) ;
2006-08-24 04:19:25 +02:00
}
2007-03-06 23:43:32 +01:00
public static final kelondroTree open ( File file , boolean useNodeCache , long preloadTime , kelondroRow rowdef , int txtProps , int txtPropsWidth ) {
2006-08-24 04:19:25 +02:00
// opens new or existing file; in case that any error occur the file is deleted again and it is tried to create the file again
// if that fails, the method returns null
try {
2007-03-06 23:43:32 +01:00
return new kelondroTree ( file , useNodeCache , preloadTime , rowdef , txtProps , txtPropsWidth ) ;
2006-08-24 04:19:25 +02:00
} catch ( IOException e ) {
file . delete ( ) ;
try {
2007-03-06 23:43:32 +01:00
return new kelondroTree ( file , useNodeCache , preloadTime , rowdef , txtProps , txtPropsWidth ) ;
2006-08-24 04:19:25 +02:00
} catch ( IOException ee ) {
log . severe ( " cannot open or create file " + file . toString ( ) ) ;
e . printStackTrace ( ) ;
ee . printStackTrace ( ) ;
return null ;
}
}
}
2007-03-06 23:43:32 +01:00
public kelondroTree ( kelondroRA ra , String filename , boolean useNodeCache , long preloadTime , kelondroRow rowdef , boolean exitOnFail ) {
2005-12-06 23:30:15 +01:00
// this creates a new tree within a kelondroRA
2007-03-06 23:43:32 +01:00
this ( ra , filename , useNodeCache , preloadTime , rowdef , new kelondroNaturalOrder ( true ) , rowdef . columns ( ) /* txtProps */ , 80 /* txtPropWidth */ , exitOnFail ) ;
2005-04-07 21:19:42 +02:00
}
2005-12-06 23:30:15 +01:00
2008-01-11 01:12:01 +01:00
public kelondroTree ( kelondroRA ra , String filename , boolean useNodeCache , long preloadTime , kelondroRow rowdef , kelondroByteOrder objectOrder , int txtProps , int txtPropsWidth , boolean exitOnFail ) {
2005-12-06 23:30:15 +01:00
// this creates a new tree within a kelondroRA
2007-03-06 23:43:32 +01:00
super ( ra , filename , useNodeCache , preloadTime ,
2006-06-02 14:45:57 +02:00
thisOHBytes , thisOHHandles , rowdef ,
2006-05-08 15:37:27 +02:00
thisFHandles , txtProps , txtPropsWidth , exitOnFail ) ;
2005-12-13 00:59:58 +01:00
try {
setHandle ( root , null ) ; // define the root value
} catch ( IOException e ) {
super . logFailure ( " cannot set root handle / " + e . getMessage ( ) ) ;
if ( exitOnFail ) System . exit ( - 1 ) ;
throw new RuntimeException ( " cannot set root handle / " + e . getMessage ( ) ) ;
}
2006-01-31 03:01:32 +01:00
super . setLogger ( log ) ;
2008-01-11 01:12:01 +01:00
this . loopDetectionOrder = new kelondroByteOrder . StringOrder ( rowdef . objectOrder ) ;
2005-04-07 21:19:42 +02:00
}
2006-05-10 11:08:42 +02:00
2007-03-06 23:43:32 +01:00
public kelondroTree ( kelondroRA ra , String filename , boolean useNodeCache , long preloadTime ) throws IOException {
2005-12-06 23:30:15 +01:00
// this opens a file with an existing tree in a kelondroRA
2007-03-06 23:43:32 +01:00
super ( ra , filename , useNodeCache , preloadTime ) ;
2006-01-31 03:01:32 +01:00
super . setLogger ( log ) ;
2006-05-09 12:03:12 +02:00
}
2006-10-16 17:04:16 +02:00
2007-04-05 12:14:48 +02:00
public void reset ( ) throws IOException {
super . reset ( ) ;
setHandle ( root , null ) ;
}
2007-08-03 13:44:58 +02:00
private void commitNode ( kelondroNode n ) throws IOException {
//kelondroHandle left = n.getOHHandle(leftchild);
//kelondroHandle right = n.getOHHandle(rightchild);
n . commit ( ) ;
2005-09-15 22:45:51 +02:00
}
2005-07-27 14:51:00 +02:00
2007-01-08 14:13:30 +01:00
public boolean has ( byte [ ] key ) throws IOException {
throw new UnsupportedOperationException ( " has should not be used with kelondroTree. " ) ;
}
2005-04-07 21:19:42 +02:00
// Returns the value to which this map maps the specified key.
2006-06-01 01:31:46 +02:00
public kelondroRow . Entry get ( byte [ ] key ) throws IOException {
2006-10-24 15:48:16 +02:00
kelondroRow . Entry result ;
2006-06-01 01:31:46 +02:00
synchronized ( writeSearchObj ) {
writeSearchObj . process ( key ) ;
if ( writeSearchObj . found ( ) ) {
result = row ( ) . newEntry ( writeSearchObj . getMatcher ( ) . getValueRow ( ) ) ;
} else {
result = null ;
}
}
return result ;
}
2008-01-20 02:22:46 +01:00
public ArrayList < kelondroRowSet > removeDoubles ( ) {
// this data structure cannot have doubles; return empty array
return new ArrayList < kelondroRowSet > ( ) ;
}
2005-04-07 21:19:42 +02:00
public class Search {
2006-01-22 01:50:05 +01:00
// a search object combines the results of a search in the tree, which are
// - the searched object is found, an index pointing to the node can be returned
// - the object was not found, an index pointing to an appropriate possible parent node
// can be returned, together with the information wether the new key shall
// be left or right child.
2005-04-07 21:19:42 +02:00
2007-08-03 13:44:58 +02:00
private CacheNode thenode , parentnode ;
2006-01-22 01:50:05 +01:00
private boolean found ; // property if node was found
private byte child ; // -1: left child; 0: root node; 1: right child
2005-04-07 21:19:42 +02:00
2006-10-02 02:13:19 +02:00
// temporary variables
2007-08-03 13:44:58 +02:00
private kelondroHandle thisHandle ;
2007-12-28 04:39:36 +01:00
String keybuffer ;
2006-10-02 02:13:19 +02:00
2006-01-22 01:50:05 +01:00
protected Search ( ) {
}
protected void process ( byte [ ] key ) throws IOException {
// searchs the database for the key and stores the result in the thisHandle
// if the key was found, then found=true, thisHandle and leftchild is set;
// else found=false and thisHandle and leftchild is undefined
thisHandle = getHandle ( root ) ;
2005-04-07 21:19:42 +02:00
parentnode = null ;
if ( key = = null ) {
2006-10-02 02:13:19 +02:00
throw new kelondroException ( " startet search process with key == null " ) ;
/ *
2005-04-07 21:19:42 +02:00
child = 0 ;
if ( thisHandle = = null ) {
thenode = null ;
found = false ;
} else {
thenode = getNode ( thisHandle , null , 0 ) ;
found = true ;
}
2006-10-02 02:13:19 +02:00
return ;
* /
}
thenode = null ;
child = 0 ;
found = false ;
int c ;
2007-12-28 04:39:36 +01:00
TreeSet < String > visitedNodeKeys = new TreeSet < String > ( loopDetectionOrder ) ; // to detect loops
2006-10-02 02:13:19 +02:00
// System.out.println("Starting Compare Loop in Database " + filename); // debug
while ( thisHandle ! = null ) {
try {
parentnode = thenode ;
2007-08-03 13:44:58 +02:00
thenode = new CacheNode ( thisHandle , thenode , ( child = = - 1 ) ? leftchild : rightchild , true ) ;
2006-10-02 02:13:19 +02:00
} catch ( IllegalArgumentException e ) {
logWarning ( " kelondroTree.Search.process: fixed a broken handle " ) ;
found = false ;
return ;
}
if ( thenode = = null ) throw new kelondroException ( filename , " kelondroTree.Search.process: thenode==null " ) ;
2007-12-28 04:39:36 +01:00
keybuffer = new String ( thenode . getKey ( ) ) ;
2006-10-02 02:13:19 +02:00
if ( keybuffer = = null ) {
// this is an error. distinguish two cases:
// 1. thenode is a leaf node. Then this error can be fixed if we can consider this node as a good node to be replaced with a new value
// 2. thenode is not a leaf node. An exception must be thrown
if ( ( thenode . getOHHandle ( leftchild ) = = null ) & & ( thenode . getOHHandle ( rightchild ) = = null ) ) {
// case 1: recover
deleteNode ( thisHandle ) ;
thenode = parentnode ;
2005-11-15 02:20:05 +01:00
found = false ;
return ;
2006-10-02 02:13:19 +02:00
} else {
// case 2: fail
throw new kelondroException ( " found key during search process with key == null " ) ;
2006-08-16 21:49:31 +02:00
}
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
if ( visitedNodeKeys . contains ( keybuffer ) ) {
2006-10-02 02:13:19 +02:00
// we have loops in the database.
// to fix this, all affected nodes must be patched
thenode . setOHByte ( magic , ( byte ) 1 ) ;
thenode . setOHByte ( balance , ( byte ) 0 ) ;
thenode . setOHHandle ( parent , null ) ;
thenode . setOHHandle ( leftchild , null ) ;
thenode . setOHHandle ( rightchild , null ) ;
2007-08-03 13:44:58 +02:00
thenode . commit ( ) ;
2006-10-02 02:13:19 +02:00
logWarning ( " kelondroTree.Search.process: database contains loops; the loop-nodes have been auto-fixed " ) ;
found = false ;
return ;
}
// System.out.println("Comparing key = '" + new String(key) + "' with '" + otherkey + "':"); // debug
2008-01-11 01:12:01 +01:00
c = row ( ) . objectOrder . compare ( key , keybuffer . getBytes ( ) ) ;
2006-10-02 02:13:19 +02:00
// System.out.println(c); // debug
if ( c = = 0 ) {
found = true ;
// System.out.println("DEBUG: search for " + new String(key) + " ended with status=" + ((found) ? "found" : "not-found") + ", node=" + ((thenode == null) ? "NULL" : thenode.toString()) + ", parent=" + ((parentnode == null) ? "NULL" : parentnode.toString()));
return ;
} else if ( c < 0 ) {
child = - 1 ;
thisHandle = thenode . getOHHandle ( leftchild ) ;
} else {
child = 1 ;
thisHandle = thenode . getOHHandle ( rightchild ) ;
}
2007-12-27 18:56:59 +01:00
visitedNodeKeys . add ( keybuffer ) ;
2005-04-07 21:19:42 +02:00
}
2006-03-17 15:20:04 +01:00
// System.out.println("DEBUG: search for " + new String(key) + " ended with status=" + ((found) ? "found" : "not-found") + ", node=" + ((thenode == null) ? "NULL" : thenode.toString()) + ", parent=" + ((parentnode == null) ? "NULL" : parentnode.toString()));
2006-01-22 01:50:05 +01:00
// we reached a node where we must insert the new value
2005-08-12 16:06:47 +02:00
// the parent of this new value can be obtained by getParent()
2006-01-22 01:50:05 +01:00
// all values are set, just return
}
2005-04-07 21:19:42 +02:00
2006-10-02 02:13:19 +02:00
public boolean found ( ) {
return found ;
}
2005-04-07 21:19:42 +02:00
2007-08-03 13:44:58 +02:00
public CacheNode getMatcher ( ) {
2006-10-02 02:13:19 +02:00
if ( found ) return thenode ;
throw new IllegalArgumentException ( " wrong access of matcher " ) ;
}
2005-04-07 21:19:42 +02:00
2007-08-03 13:44:58 +02:00
public CacheNode getParent ( ) {
2006-10-02 02:13:19 +02:00
if ( found ) return parentnode ;
return thenode ;
}
2005-04-07 21:19:42 +02:00
2006-10-02 02:13:19 +02:00
public boolean isRoot ( ) {
if ( found ) throw new IllegalArgumentException ( " wrong access of isRoot " ) ;
return ( child = = 0 ) ;
}
2005-04-07 21:19:42 +02:00
2006-10-02 02:13:19 +02:00
public boolean isLeft ( ) {
if ( found ) throw new IllegalArgumentException ( " wrong access of leftchild " ) ;
return ( child = = - 1 ) ;
}
public boolean isRight ( ) {
if ( found ) throw new IllegalArgumentException ( " wrong access of leftchild " ) ;
return ( child = = 1 ) ;
}
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
public synchronized boolean isChild ( kelondroNode childn , kelondroNode parentn , int child ) {
2006-06-01 01:31:46 +02:00
if ( childn = = null ) throw new IllegalArgumentException ( " isLeftChild: Node parameter is NULL " ) ;
2007-08-03 13:44:58 +02:00
kelondroHandle lc = parentn . getOHHandle ( child ) ;
2006-06-01 01:31:46 +02:00
if ( lc = = null ) return false ;
return ( lc . equals ( childn . handle ( ) ) ) ;
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
public synchronized void putMultiple ( List < Entry > rows ) throws IOException {
Iterator < Entry > i = rows . iterator ( ) ;
while ( i . hasNext ( ) ) put ( i . next ( ) ) ;
2007-02-25 22:06:26 +01:00
}
2006-10-13 01:14:41 +02:00
public kelondroRow . Entry put ( kelondroRow . Entry row , Date entryDate ) throws IOException {
return put ( row ) ;
}
2006-06-02 14:45:57 +02:00
public kelondroRow . Entry put ( kelondroRow . Entry newrow ) throws IOException {
2006-10-24 15:48:16 +02:00
assert ( newrow ! = null ) ;
assert ( newrow . columns ( ) = = row ( ) . columns ( ) ) ;
2007-11-07 23:38:09 +01:00
assert ( ! ( serverLog . allZero ( newrow . getPrimaryKeyBytes ( ) ) ) ) ;
2007-04-03 14:10:12 +02:00
assert newrow . objectsize ( ) < = super . row ( ) . objectsize ;
2006-10-13 01:14:41 +02:00
// Associates the specified value with the specified key in this map
2006-05-31 00:39:19 +02:00
kelondroRow . Entry result = null ;
2005-12-08 13:30:55 +01:00
//writeLock.stay(2000, 1000);
2005-10-27 01:05:20 +02:00
// first try to find the key element in the database
synchronized ( writeSearchObj ) {
2006-06-02 14:45:57 +02:00
writeSearchObj . process ( newrow . getColBytes ( 0 ) ) ;
2005-10-27 01:05:20 +02:00
if ( writeSearchObj . found ( ) ) {
// a node with this key exist. simply overwrite the content and return old content
2007-08-03 13:44:58 +02:00
kelondroNode e = writeSearchObj . getMatcher ( ) ;
result = row ( ) . newEntry ( e . getValueRow ( ) ) ;
e . setValueRow ( newrow . bytes ( ) ) ;
2005-10-27 01:05:20 +02:00
commitNode ( e ) ;
} else if ( writeSearchObj . isRoot ( ) ) {
// a node with this key does not exist and there is no node at all
// this therefore creates the root node if an only if there was no root Node yet
if ( getHandle ( root ) ! = null )
throw new kelondroException ( filename , " tried to create root node twice " ) ;
// we dont have any Nodes in the file, so start here to create one
2007-08-03 13:44:58 +02:00
kelondroNode e = new CacheNode ( newrow . bytes ( ) ) ;
2005-10-27 01:05:20 +02:00
// write the propetries
e . setOHByte ( magic , ( byte ) 1 ) ;
e . setOHByte ( balance , ( byte ) 0 ) ;
e . setOHHandle ( parent , null ) ;
e . setOHHandle ( leftchild , null ) ;
e . setOHHandle ( rightchild , null ) ;
// do updates
2007-08-03 13:44:58 +02:00
e . commit ( ) ;
2005-10-27 01:05:20 +02:00
setHandle ( root , e . handle ( ) ) ;
2005-12-08 13:30:55 +01:00
result = null ;
2005-10-27 01:05:20 +02:00
} else {
// a node with this key does not exist
// this creates a new node if there is already at least a root node
// to create the new node, it is necessary to assign it to a parent
// it must also be defined weather this new node is a left child of the
// parent or not. It is checked if the parent node already has a child on
// that side, but not if the assigned position is appropriate.
// create new node and assign values
2007-08-03 13:44:58 +02:00
CacheNode parentNode = writeSearchObj . getParent ( ) ;
CacheNode theNode = new CacheNode ( newrow . bytes ( ) ) ;
2005-10-27 01:05:20 +02:00
theNode . setOHByte ( 0 , ( byte ) 1 ) ; // fresh magic
theNode . setOHByte ( 1 , ( byte ) 0 ) ; // fresh balance
theNode . setOHHandle ( parent , parentNode . handle ( ) ) ;
theNode . setOHHandle ( leftchild , null ) ;
theNode . setOHHandle ( rightchild , null ) ;
2007-08-03 13:44:58 +02:00
theNode . commit ( ) ;
2005-10-27 01:05:20 +02:00
// check consistency and link new node to parent node
byte parentBalance ;
if ( writeSearchObj . isLeft ( ) ) {
2007-08-03 13:44:58 +02:00
if ( parentNode . getOHHandle ( leftchild ) ! = null ) throw new kelondroException ( filename , " tried to create leftchild node twice. parent= " + new String ( parentNode . getKey ( ) ) + " , leftchild= " + new String ( new CacheNode ( parentNode . getOHHandle ( leftchild ) , ( CacheNode ) null , 0 , true ) . getKey ( ) ) ) ;
2005-10-27 01:05:20 +02:00
parentNode . setOHHandle ( leftchild , theNode . handle ( ) ) ;
} else if ( writeSearchObj . isRight ( ) ) {
2007-08-03 13:44:58 +02:00
if ( parentNode . getOHHandle ( rightchild ) ! = null ) throw new kelondroException ( filename , " tried to create rightchild node twice. parent= " + new String ( parentNode . getKey ( ) ) + " , rightchild= " + new String ( new CacheNode ( parentNode . getOHHandle ( rightchild ) , ( CacheNode ) null , 0 , true ) . getKey ( ) ) ) ;
2005-10-27 01:05:20 +02:00
parentNode . setOHHandle ( rightchild , theNode . handle ( ) ) ;
} else {
throw new kelondroException ( filename , " neither left nor right child " ) ;
}
2005-09-15 22:45:51 +02:00
commitNode ( parentNode ) ;
2005-10-27 01:05:20 +02:00
// now update recursively the node balance of the parentNode
// what do we have:
// - new Node, called 'theNode'
// - parent Node
// set balance factor in parent node(s)
boolean increasedHight = true ;
String path = " " ;
byte prevHight ;
2007-08-03 13:44:58 +02:00
kelondroHandle parentSideHandle ;
2005-10-27 01:05:20 +02:00
while ( increasedHight ) {
// update balance
parentBalance = parentNode . getOHByte ( balance ) ; // {magic, balance}
prevHight = parentBalance ;
parentSideHandle = parentNode . getOHHandle ( leftchild ) ;
if ( ( parentSideHandle ! = null ) & & ( parentSideHandle . equals ( theNode . handle ( ) ) ) ) {
// is left child
parentBalance + + ;
path = " L " + path ;
}
parentSideHandle = parentNode . getOHHandle ( rightchild ) ;
if ( ( parentSideHandle ! = null ) & & ( parentSideHandle . equals ( theNode . handle ( ) ) ) ) {
// is right child
parentBalance - - ;
path = " R " + path ;
}
increasedHight = ( ( java . lang . Math . abs ( ( int ) parentBalance ) - java . lang . Math . abs ( ( int ) prevHight ) ) > 0 ) ;
parentNode . setOHByte ( balance , parentBalance ) ;
commitNode ( parentNode ) ;
// here we either stop because we had no increased hight,
// or we have a balance greater then 1 or less than -1 and we do rotation
// or we crawl up the tree and change the next balance
if ( ! ( increasedHight ) ) break ; // finished
// check rotation need
if ( java . lang . Math . abs ( ( int ) parentBalance ) > 1 ) {
// rotate and stop then
//System.out.println("* DB DEBUG: " + path.substring(0,2) + " ROTATION AT NODE " + parentNode.handle().toString() + ": BALANCE=" + parentOHByte[balance]);
if ( path . startsWith ( " LL " ) ) {
LL_RightRotation ( parentNode , theNode ) ;
break ;
}
if ( path . startsWith ( " RR " ) ) {
RR_LeftRotation ( parentNode , theNode ) ;
break ;
}
if ( path . startsWith ( " RL " ) ) {
2007-08-03 13:44:58 +02:00
kelondroHandle parentHandle = parentNode . handle ( ) ;
LL_RightRotation ( theNode , new CacheNode ( theNode . getOHHandle ( leftchild ) , theNode , leftchild , false ) ) ;
parentNode = new CacheNode ( parentHandle , null , 0 , false ) ; // reload the parent node
RR_LeftRotation ( parentNode , new CacheNode ( parentNode . getOHHandle ( rightchild ) , parentNode , rightchild , false ) ) ;
2005-10-27 01:05:20 +02:00
break ;
}
if ( path . startsWith ( " LR " ) ) {
2007-08-03 13:44:58 +02:00
kelondroHandle parentHandle = parentNode . handle ( ) ;
RR_LeftRotation ( theNode , new CacheNode ( theNode . getOHHandle ( rightchild ) , theNode , rightchild , false ) ) ;
parentNode = new CacheNode ( parentHandle , null , 0 , false ) ; // reload the parent node
LL_RightRotation ( parentNode , new CacheNode ( parentNode . getOHHandle ( leftchild ) , parentNode , leftchild , false ) ) ;
2005-10-27 01:05:20 +02:00
break ;
}
break ;
}
2006-08-16 21:49:31 +02:00
// crawl up the tree
if ( parentNode . getOHHandle ( parent ) = = null ) break ; // root reached: stop
theNode = parentNode ;
2007-08-03 13:44:58 +02:00
parentNode = new CacheNode ( parentNode . getOHHandle ( parent ) , null , 0 , false ) ;
2005-10-27 01:05:20 +02:00
}
2006-08-16 21:49:31 +02:00
result = null ; // that means: no previous stored value present
2005-10-27 01:05:20 +02:00
}
}
2005-12-08 13:30:55 +01:00
//writeLock.release();
2006-06-02 14:45:57 +02:00
return result ;
2005-04-07 21:19:42 +02:00
}
2006-10-19 23:14:37 +02:00
public synchronized void addUnique ( kelondroRow . Entry row ) throws IOException {
2006-10-26 15:50:50 +02:00
this . put ( row ) ;
2006-10-19 23:14:37 +02:00
}
public synchronized void addUnique ( kelondroRow . Entry row , Date entryDate ) throws IOException {
2006-10-26 15:50:50 +02:00
this . put ( row , entryDate ) ;
2006-10-19 23:14:37 +02:00
}
2007-12-27 18:56:59 +01:00
public synchronized void addUniqueMultiple ( List < kelondroRow . Entry > rows ) throws IOException {
Iterator < kelondroRow . Entry > i = rows . iterator ( ) ;
while ( i . hasNext ( ) ) addUnique ( i . next ( ) ) ;
2007-02-27 16:54:02 +01:00
}
2007-08-03 13:44:58 +02:00
private void assignChild ( kelondroNode parentNode , kelondroNode childNode , int childType ) throws IOException {
2006-05-10 11:08:42 +02:00
parentNode . setOHHandle ( childType , childNode . handle ( ) ) ;
2005-09-15 22:45:51 +02:00
childNode . setOHHandle ( parent , parentNode . handle ( ) ) ;
commitNode ( parentNode ) ;
commitNode ( childNode ) ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
private void replace ( kelondroNode oldNode , kelondroNode oldNodeParent , kelondroNode newNode ) throws IOException {
2005-04-07 21:19:42 +02:00
// this routine looks where the oldNode is connected to, and replaces
// the anchor's link to the oldNode by the newNode-link
// the new link gets the anchor as parent link assigned
// the oldNode will not be updated, so this must be done outside this routine
// distinguish case where the oldNode is the root node
if ( oldNodeParent = = null ) {
// this is the root, update root
setHandle ( root , newNode . handle ( ) ) ;
// update new Node
2005-09-15 22:45:51 +02:00
newNode . setOHHandle ( parent , null ) ;
commitNode ( newNode ) ;
2005-04-07 21:19:42 +02:00
} else {
// not the root, find parent
// ok, we have the parent, but for updating the child link we must know
// if the oldNode was left or right child
2007-08-03 13:44:58 +02:00
kelondroHandle parentSideHandle = oldNodeParent . getOHHandle ( leftchild ) ;
if ( ( parentSideHandle ! = null ) & & ( parentSideHandle . equals ( oldNode . handle ( ) ) ) ) {
// update left node from parent
oldNodeParent . setOHHandle ( leftchild , newNode . handle ( ) ) ;
2005-04-07 21:19:42 +02:00
}
2005-09-15 22:45:51 +02:00
parentSideHandle = oldNodeParent . getOHHandle ( rightchild ) ;
if ( ( parentSideHandle ! = null ) & & ( parentSideHandle . equals ( oldNode . handle ( ) ) ) ) {
2007-08-03 13:44:58 +02:00
// update right node from parent
oldNodeParent . setOHHandle ( rightchild , newNode . handle ( ) ) ;
2005-04-07 21:19:42 +02:00
}
2005-09-15 22:45:51 +02:00
commitNode ( oldNodeParent ) ;
2005-04-07 21:19:42 +02:00
// update new Node
2005-09-15 22:45:51 +02:00
newNode . setOHHandle ( parent , oldNodeParent . handle ( ) ) ;
2007-08-03 13:44:58 +02:00
commitNode ( newNode ) ;
2005-04-07 21:19:42 +02:00
}
// finished. remember that we did not set the links to the oldNode
// we have also not set the children of the newNode.
// this must be done somewhere outside this function.
// if the oldNode is not needed any more, it can be disposed (check childs first).
}
private static byte max0 ( byte b ) {
2006-08-16 21:49:31 +02:00
if ( b > 0 ) return b ;
return 0 ;
2005-04-07 21:19:42 +02:00
}
private static byte min0 ( byte b ) {
2006-08-16 21:49:31 +02:00
if ( b < 0 ) return b ;
return 0 ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
private void LL_RightRotation ( kelondroNode parentNode , CacheNode childNode ) throws IOException {
2006-05-10 11:08:42 +02:00
// replace the parent node; the parent is afterwards unlinked
2007-08-03 13:44:58 +02:00
kelondroHandle p2Handle = parentNode . getOHHandle ( parent ) ;
kelondroNode p2Node = ( p2Handle = = null ) ? null : new CacheNode ( p2Handle , null , 0 , false ) ;
2005-04-07 21:19:42 +02:00
replace ( parentNode , p2Node , childNode ) ;
2006-05-10 11:08:42 +02:00
// set the left son of the parent to the right son of the childNode
2007-08-03 13:44:58 +02:00
kelondroHandle childOfChild = childNode . getOHHandle ( rightchild ) ;
2006-05-10 11:08:42 +02:00
if ( childOfChild = = null ) {
parentNode . setOHHandle ( leftchild , null ) ;
} else {
2007-08-03 13:44:58 +02:00
assignChild ( parentNode , new CacheNode ( childOfChild , childNode , rightchild , false ) , leftchild ) ;
2006-05-10 11:08:42 +02:00
}
2005-04-07 21:19:42 +02:00
2006-05-10 11:08:42 +02:00
// link the old parent node as the right child of childNode
assignChild ( childNode , parentNode , rightchild ) ;
2005-04-07 21:19:42 +02:00
2006-05-10 11:08:42 +02:00
// - newBal(parent) = oldBal(parent) - 1 - max(oldBal(leftChild), 0)
// - newBal(leftChild) = oldBal(leftChild) - 1 + min(newBal(parent), 0)
byte parentBalance = parentNode . getOHByte ( balance ) ;
byte childBalance = childNode . getOHByte ( balance ) ;
byte oldBalParent = parentBalance ;
byte oldBalChild = childBalance ;
parentBalance = ( byte ) ( oldBalParent - 1 - max0 ( oldBalChild ) ) ;
childBalance = ( byte ) ( oldBalChild - 1 + min0 ( parentBalance ) ) ;
parentNode . setOHByte ( balance , parentBalance ) ;
2005-09-15 22:45:51 +02:00
childNode . setOHByte ( balance , childBalance ) ;
commitNode ( parentNode ) ;
commitNode ( childNode ) ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
private void RR_LeftRotation ( kelondroNode parentNode , CacheNode childNode ) throws IOException {
2006-05-10 11:08:42 +02:00
// replace the parent node; the parent is afterwards unlinked
2007-08-03 13:44:58 +02:00
kelondroHandle p2Handle = parentNode . getOHHandle ( parent ) ;
kelondroNode p2Node = ( p2Handle = = null ) ? null : new CacheNode ( p2Handle , null , 0 , false ) ;
2006-05-10 11:08:42 +02:00
replace ( parentNode , p2Node , childNode ) ;
2005-04-07 21:19:42 +02:00
2006-05-10 11:08:42 +02:00
// set the left son of the parent to the right son of the childNode
2007-08-03 13:44:58 +02:00
kelondroHandle childOfChild = childNode . getOHHandle ( leftchild ) ;
2006-05-10 11:08:42 +02:00
if ( childOfChild = = null ) {
parentNode . setOHHandle ( rightchild , null ) ;
} else {
2007-08-03 13:44:58 +02:00
assignChild ( parentNode , new CacheNode ( childOfChild , childNode , leftchild , false ) , rightchild ) ;
2006-05-10 11:08:42 +02:00
}
2005-04-07 21:19:42 +02:00
2006-05-10 11:08:42 +02:00
// link the old parent node as the left child of childNode
assignChild ( childNode , parentNode , leftchild ) ;
2005-04-07 21:19:42 +02:00
2006-05-10 11:08:42 +02:00
// - newBal(parent) = oldBal(parent) + 1 - min(oldBal(rightChild), 0)
// - newBal(rightChild) = oldBal(rightChild) + 1 + max(newBal(parent), 0)
byte parentBalance = parentNode . getOHByte ( balance ) ;
byte childBalance = childNode . getOHByte ( balance ) ;
byte oldBalParent = parentBalance ;
byte oldBalChild = childBalance ;
parentBalance = ( byte ) ( oldBalParent + 1 - min0 ( oldBalChild ) ) ;
childBalance = ( byte ) ( oldBalChild + 1 + max0 ( parentBalance ) ) ;
parentNode . setOHByte ( balance , parentBalance ) ;
childNode . setOHByte ( balance , childBalance ) ;
2005-09-15 22:45:51 +02:00
commitNode ( parentNode ) ;
commitNode ( childNode ) ;
2005-04-07 21:19:42 +02:00
}
// Associates the specified value with the specified key in this map
2005-12-13 12:58:55 +01:00
public byte [ ] put ( byte [ ] key , byte [ ] value ) throws IOException {
2006-06-02 14:45:57 +02:00
kelondroRow . Entry row = row ( ) . newEntry ( new byte [ ] [ ] { key , value } ) ;
kelondroRow . Entry ret = put ( row ) ;
if ( ret = = null ) return null ; else return ret . getColBytes ( 0 ) ;
2005-04-07 21:19:42 +02:00
}
// Removes the mapping for this key from this map if present (optional operation).
2007-10-22 17:26:47 +02:00
public kelondroRow . Entry remove ( byte [ ] key , boolean keepOrder ) throws IOException {
// keepOrder cannot have any effect: the order inside the database file cannot be maintained, but iteration over objects will always maintain the object order
// therefore keepOrder should be true, because the effect is always given, while the data structure does not maintain order
2006-08-21 03:51:27 +02:00
// delete from database
2005-10-27 01:05:20 +02:00
synchronized ( writeSearchObj ) {
writeSearchObj . process ( key ) ;
if ( writeSearchObj . found ( ) ) {
2007-08-03 13:44:58 +02:00
CacheNode result = writeSearchObj . getMatcher ( ) ;
2006-05-31 00:39:19 +02:00
kelondroRow . Entry values = row ( ) . newEntry ( result . getValueRow ( ) ) ;
2005-10-27 01:05:20 +02:00
remove ( result , writeSearchObj . getParent ( ) ) ;
2006-06-02 14:45:57 +02:00
return values ;
2005-10-27 01:05:20 +02:00
} else {
return null ;
}
}
2005-04-07 21:19:42 +02:00
}
2006-10-16 17:04:16 +02:00
public kelondroRow . Entry removeOne ( ) throws IOException {
// removes just any entry and removes that entry
synchronized ( writeSearchObj ) {
2007-08-03 13:44:58 +02:00
CacheNode theOne = lastNode ( ) ;
2006-10-16 17:04:16 +02:00
kelondroRow . Entry values = row ( ) . newEntry ( theOne . getValueRow ( ) ) ;
remove ( theOne , null ) ;
return values ;
}
}
2005-11-06 22:06:55 +01:00
public synchronized void removeAll ( ) throws IOException {
2005-04-07 21:19:42 +02:00
while ( size ( ) > 0 ) remove ( lastNode ( ) , null ) ;
}
2007-08-03 13:44:58 +02:00
private void remove ( CacheNode node , kelondroNode parentOfNode ) throws IOException {
// there are three cases when removing a node
// - the node is a leaf - it can be removed easily
// - the node has one child - the child replaces the node
// - the node has two childs - it can be replaced either
// by the greatest node of the left child or the smallest
// node of the right child
2005-04-07 21:19:42 +02:00
2007-08-03 13:44:58 +02:00
kelondroNode childnode ;
if ( ( node . getOHHandle ( leftchild ) = = null ) & & ( node . getOHHandle ( rightchild ) = = null ) ) {
2005-04-07 21:19:42 +02:00
// easy case: the node is a leaf
if ( parentOfNode = = null ) {
// this is the root!
setHandle ( root , null ) ;
} else {
2007-08-03 13:44:58 +02:00
kelondroHandle h = parentOfNode . getOHHandle ( leftchild ) ;
if ( ( h ! = null ) & & ( h . equals ( node . handle ( ) ) ) ) parentOfNode . setOHHandle ( leftchild , null ) ;
h = parentOfNode . getOHHandle ( rightchild ) ;
if ( ( h ! = null ) & & ( h . equals ( node . handle ( ) ) ) ) parentOfNode . setOHHandle ( rightchild , null ) ;
commitNode ( parentOfNode ) ;
2005-04-07 21:19:42 +02:00
}
2005-09-15 22:45:51 +02:00
} else if ( ( node . getOHHandle ( leftchild ) ! = null ) & & ( node . getOHHandle ( rightchild ) = = null ) ) {
2007-08-03 13:44:58 +02:00
replace ( node , parentOfNode , new CacheNode ( node . getOHHandle ( leftchild ) , node , leftchild , false ) ) ;
2005-09-15 22:45:51 +02:00
} else if ( ( node . getOHHandle ( leftchild ) = = null ) & & ( node . getOHHandle ( rightchild ) ! = null ) ) {
2007-08-03 13:44:58 +02:00
replace ( node , parentOfNode , new CacheNode ( node . getOHHandle ( rightchild ) , node , rightchild , false ) ) ;
2005-04-07 21:19:42 +02:00
} else {
// difficult case: node has two children
2007-08-03 13:44:58 +02:00
CacheNode repl = lastNode ( new CacheNode ( node . getOHHandle ( leftchild ) , node , leftchild , false ) ) ;
2005-04-07 21:19:42 +02:00
//System.out.println("last node is " + repl.toString());
// we remove that replacement node and put it where the node was
// this seems to be recursive, but is not since the replacement
// node cannot have two children (it would not have been the smallest or greatest)
2007-08-03 13:44:58 +02:00
kelondroNode n ;
kelondroHandle h ;
2005-04-07 21:19:42 +02:00
// remove leaf
2005-09-15 22:45:51 +02:00
if ( ( repl . getOHHandle ( leftchild ) = = null ) & & ( repl . getOHHandle ( rightchild ) = = null ) ) {
2005-04-07 21:19:42 +02:00
// the replacement cannot be the root, so simply remove from parent node
2007-08-03 13:44:58 +02:00
n = new CacheNode ( repl . getOHHandle ( parent ) , null , 0 , false ) ; // parent node of replacement node
2005-09-15 22:45:51 +02:00
h = n . getOHHandle ( leftchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( leftchild , null ) ;
h = n . getOHHandle ( rightchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( rightchild , null ) ;
commitNode ( n ) ;
} else if ( ( repl . getOHHandle ( leftchild ) ! = null ) & & ( repl . getOHHandle ( rightchild ) = = null ) ) {
2005-04-07 21:19:42 +02:00
try {
2007-08-03 13:44:58 +02:00
childnode = new CacheNode ( repl . getOHHandle ( leftchild ) , repl , leftchild , false ) ;
replace ( repl , new CacheNode ( repl . getOHHandle ( parent ) , null , 0 , false ) , childnode ) ;
2005-04-07 21:19:42 +02:00
} catch ( IllegalArgumentException e ) {
// now treat the situation as if that link had been null before
2007-08-03 13:44:58 +02:00
n = new CacheNode ( repl . getOHHandle ( parent ) , null , 0 , false ) ; // parent node of replacement node
2005-09-15 22:45:51 +02:00
h = n . getOHHandle ( leftchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( leftchild , null ) ;
h = n . getOHHandle ( rightchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( rightchild , null ) ;
commitNode ( n ) ;
2005-04-07 21:19:42 +02:00
}
2005-09-15 22:45:51 +02:00
} else if ( ( repl . getOHHandle ( leftchild ) = = null ) & & ( repl . getOHHandle ( rightchild ) ! = null ) ) {
2005-04-07 21:19:42 +02:00
try {
2007-08-03 13:44:58 +02:00
childnode = new CacheNode ( repl . getOHHandle ( rightchild ) , repl , rightchild , false ) ;
replace ( repl , new CacheNode ( repl . getOHHandle ( parent ) , null , 0 , false ) , childnode ) ;
2005-04-07 21:19:42 +02:00
} catch ( IllegalArgumentException e ) {
// now treat the situation as if that link had been null before
2007-08-03 13:44:58 +02:00
n = new CacheNode ( repl . getOHHandle ( parent ) , null , 0 , false ) ; // parent node of replacement node
2005-09-15 22:45:51 +02:00
h = n . getOHHandle ( leftchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( leftchild , null ) ;
h = n . getOHHandle ( rightchild ) ;
if ( ( h ! = null ) & & ( h . equals ( repl . handle ( ) ) ) ) n . setOHHandle ( rightchild , null ) ;
commitNode ( n ) ;
2005-04-07 21:19:42 +02:00
}
}
//System.out.println("node before reload is " + node.toString());
2007-08-03 13:44:58 +02:00
node = new CacheNode ( node . handle ( ) , null , 0 , false ) ; // reload the node, it is possible that it has been changed
2005-04-07 21:19:42 +02:00
//System.out.println("node after reload is " + node.toString());
// now plant in the replha node
2005-09-15 22:45:51 +02:00
byte b = node . getOHByte ( balance ) ; // save balance of disappearing node
2007-08-03 13:44:58 +02:00
kelondroHandle parentHandle = node . getOHHandle ( parent ) ;
kelondroHandle leftchildHandle = node . getOHHandle ( leftchild ) ;
kelondroHandle rightchildHandle = node . getOHHandle ( rightchild ) ;
2005-04-07 21:19:42 +02:00
replace ( node , parentOfNode , repl ) ;
2005-09-15 22:45:51 +02:00
repl . setOHByte ( balance , b ) ; // restore balance
repl . setOHHandle ( parent , parentHandle ) ; // restore handles
2007-08-03 13:44:58 +02:00
repl . setOHHandle ( leftchild , leftchildHandle ) ;
repl . setOHHandle ( rightchild , rightchildHandle ) ;
commitNode ( repl ) ;
2005-04-07 21:19:42 +02:00
// last thing to do: change uplinks of children to this new node
2005-09-15 22:45:51 +02:00
if ( leftchildHandle ! = null ) {
2007-08-03 13:44:58 +02:00
n = new CacheNode ( leftchildHandle , node , leftchild , false ) ;
n . setOHHandle ( parent , repl . handle ( ) ) ;
commitNode ( n ) ;
2005-04-07 21:19:42 +02:00
}
2005-09-15 22:45:51 +02:00
if ( rightchildHandle ! = null ) {
2007-08-03 13:44:58 +02:00
n = new CacheNode ( rightchildHandle , node , rightchild , false ) ;
n . setOHHandle ( parent , repl . handle ( ) ) ;
commitNode ( n ) ;
2005-04-07 21:19:42 +02:00
}
}
2005-09-14 12:10:49 +02:00
// move node to recycling queue
2006-12-22 16:26:29 +01:00
synchronized ( this ) {
deleteNode ( node . handle ( ) ) ;
}
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
protected CacheNode firstNode ( ) throws IOException {
kelondroHandle h = getHandle ( root ) ;
2006-08-16 21:49:31 +02:00
if ( h = = null ) return null ;
2007-08-03 13:44:58 +02:00
return firstNode ( new CacheNode ( h , null , 0 , true ) ) ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
protected CacheNode firstNode ( CacheNode node ) throws IOException {
2005-04-07 21:19:42 +02:00
if ( node = = null ) throw new IllegalArgumentException ( " firstNode: node=null " ) ;
2007-08-03 13:44:58 +02:00
kelondroHandle h = node . getOHHandle ( leftchild ) ;
2007-12-27 18:56:59 +01:00
HashSet < String > visitedNodeKeys = new HashSet < String > ( ) ; // to detect loops
2005-11-06 22:06:55 +01:00
String nodeKey ;
while ( h ! = null ) {
2005-04-07 21:19:42 +02:00
try {
2007-08-03 13:44:58 +02:00
node = new CacheNode ( h , node , leftchild , true ) ;
2005-11-06 22:06:55 +01:00
nodeKey = new String ( node . getKey ( ) ) ;
if ( visitedNodeKeys . contains ( nodeKey ) ) throw new kelondroException ( this . filename , " firstNode: database contains loops: ' " + nodeKey + " ' appears twice. " ) ;
visitedNodeKeys . add ( nodeKey ) ;
2005-04-07 21:19:42 +02:00
} catch ( IllegalArgumentException e ) {
// return what we have
return node ;
}
2005-09-15 22:45:51 +02:00
h = node . getOHHandle ( leftchild ) ;
2006-08-16 21:49:31 +02:00
}
return node ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
protected CacheNode lastNode ( ) throws IOException {
kelondroHandle h = getHandle ( root ) ;
2006-08-16 21:49:31 +02:00
if ( h = = null ) return null ;
2007-08-03 13:44:58 +02:00
return lastNode ( new CacheNode ( h , null , 0 , true ) ) ;
2005-04-07 21:19:42 +02:00
}
2007-08-03 13:44:58 +02:00
protected CacheNode lastNode ( CacheNode node ) throws IOException {
2006-08-16 21:49:31 +02:00
if ( node = = null ) throw new IllegalArgumentException ( " lastNode: node=null " ) ;
2007-08-03 13:44:58 +02:00
kelondroHandle h = node . getOHHandle ( rightchild ) ;
2007-12-27 18:56:59 +01:00
HashSet < String > visitedNodeKeys = new HashSet < String > ( ) ; // to detect loops
2005-11-06 22:06:55 +01:00
String nodeKey ;
2006-08-16 21:49:31 +02:00
while ( h ! = null ) {
2005-04-07 21:19:42 +02:00
try {
2007-08-03 13:44:58 +02:00
node = new CacheNode ( h , node , rightchild , true ) ;
2005-11-06 22:06:55 +01:00
nodeKey = new String ( node . getKey ( ) ) ;
if ( visitedNodeKeys . contains ( nodeKey ) ) throw new kelondroException ( this . filename , " lastNode: database contains loops: ' " + nodeKey + " ' appears twice. " ) ;
visitedNodeKeys . add ( nodeKey ) ;
2005-04-07 21:19:42 +02:00
} catch ( IllegalArgumentException e ) {
// return what we have
return node ;
}
2005-09-15 22:45:51 +02:00
h = node . getOHHandle ( rightchild ) ;
2006-08-16 21:49:31 +02:00
}
return node ;
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
private class nodeIterator implements Iterator < CacheNode > {
2006-03-17 15:20:04 +01:00
// we implement an iteration! (not a recursive function as the structure would suggest...)
// the iterator iterates Node objects
2007-08-03 13:44:58 +02:00
CacheNode nextNode = null ;
2006-03-17 15:20:04 +01:00
boolean up , rot ;
2007-12-27 18:56:59 +01:00
LinkedList < Object [ ] > nodeStack ;
2006-03-17 15:20:04 +01:00
int count ;
public nodeIterator ( boolean up , boolean rotating ) throws IOException {
this . count = 0 ;
this . up = up ;
this . rot = rotating ;
// initialize iterator
init ( ( up ) ? firstNode ( ) : lastNode ( ) ) ;
}
public nodeIterator ( boolean up , boolean rotating , byte [ ] firstKey , boolean including ) throws IOException {
this . count = 0 ;
this . up = up ;
this . rot = rotating ;
2005-10-27 01:05:20 +02:00
Search search = new Search ( ) ;
search . process ( firstKey ) ;
if ( search . found ( ) ) {
2006-03-17 15:20:04 +01:00
init ( search . getMatcher ( ) ) ;
2005-04-07 21:19:42 +02:00
} else {
2007-08-03 13:44:58 +02:00
CacheNode nn = search . getParent ( ) ;
2005-04-07 21:19:42 +02:00
if ( nn = = null ) {
2006-03-17 15:20:04 +01:00
this . nextNode = null ;
2005-04-07 21:19:42 +02:00
} else {
2005-08-12 16:06:47 +02:00
// the node nn may be greater or smaller than the firstKey
// depending on the ordering direction,
// we must find the next smaller or greater node
2006-03-17 15:20:04 +01:00
// this is corrected in the initializer of nodeIterator
init ( nn ) ;
2005-04-07 21:19:42 +02:00
}
}
2005-08-12 16:06:47 +02:00
2006-03-17 15:20:04 +01:00
// correct nextNode upon start
// this happens, if the start node was not proper, or could not be found
while ( ( nextNode ! = null ) & & ( nextNode . getKey ( ) ! = null ) ) {
2007-02-20 09:35:51 +01:00
int c = row ( ) . objectOrder . compare ( firstKey , nextNode . getKey ( ) ) ;
2006-03-17 15:20:04 +01:00
if ( c = = 0 ) {
if ( including ) {
break ; // correct + finished
} else {
if ( hasNext ( ) ) next ( ) ; else nextNode = null ;
break ; // corrected + finished
}
} else if ( c < 0 ) {
if ( up ) {
break ; // correct + finished
} else {
// firstKey < nextNode.getKey(): correct once
if ( hasNext ( ) ) next ( ) ; else nextNode = null ;
}
} else if ( c > 0 ) {
if ( up ) {
// firstKey > nextNode.getKey(): correct once
if ( hasNext ( ) ) next ( ) ; else nextNode = null ;
} else {
break ; // correct + finished
2006-01-12 08:24:03 +01:00
}
}
}
2005-08-12 16:06:47 +02:00
}
2007-08-03 13:44:58 +02:00
private void init ( CacheNode start ) throws IOException {
2005-05-08 16:42:09 +02:00
this . nextNode = start ;
// fill node stack for start node
2007-12-27 18:56:59 +01:00
nodeStack = new LinkedList < Object [ ] > ( ) ;
2005-05-08 16:42:09 +02:00
2007-08-03 13:44:58 +02:00
kelondroHandle searchHandle = getHandle ( root ) ;
2005-05-08 16:42:09 +02:00
if ( searchHandle = = null ) { nextNode = null ; return ; }
2007-08-03 13:44:58 +02:00
CacheNode searchNode = new CacheNode ( searchHandle , null , 0 , false ) ;
2005-05-08 16:42:09 +02:00
byte [ ] startKey = start . getKey ( ) ;
int c , ct ;
2007-02-20 09:35:51 +01:00
while ( ( c = row ( ) . objectOrder . compare ( startKey , searchNode . getKey ( ) ) ) ! = 0 ) {
2005-05-08 16:42:09 +02:00
// the current 'thisNode' is not the start node, put it on the stack
ct = ( c < 0 ) ? leftchild : rightchild ;
nodeStack . addLast ( new Object [ ] { searchNode , new Integer ( ct ) } ) ;
// go to next node
2005-09-15 22:45:51 +02:00
searchHandle = searchNode . getOHHandle ( ct ) ;
2005-11-15 02:20:05 +01:00
if ( searchHandle = = null ) throw new kelondroException ( filename , " nodeIterator.init: start node does not exist (handle null) " ) ;
2007-08-03 13:44:58 +02:00
searchNode = new CacheNode ( searchHandle , searchNode , ct , false ) ;
2005-11-15 02:20:05 +01:00
if ( searchNode = = null ) throw new kelondroException ( filename , " nodeIterator.init: start node does not exist (node null) " ) ;
2005-05-08 16:42:09 +02:00
}
// now every parent node to the start node is on the stack
2005-11-14 01:23:20 +01:00
}
2005-05-08 16:42:09 +02:00
2005-06-19 07:27:42 +02:00
public void finalize ( ) {
nextNode = null ;
nodeStack = null ;
}
2006-03-17 15:20:04 +01:00
public boolean hasNext ( ) {
2005-12-19 01:40:06 +01:00
return ( rot & & ( size ( ) > 0 ) ) | | ( nextNode ! = null ) ;
2006-03-17 15:20:04 +01:00
}
2005-05-08 16:42:09 +02:00
2007-12-27 18:56:59 +01:00
public CacheNode next ( ) {
2006-03-17 15:20:04 +01:00
count + + ;
2005-11-14 01:23:20 +01:00
if ( ( rot ) & & ( nextNode = = null ) ) try {
init ( ( up ) ? firstNode ( ) : lastNode ( ) ) ;
} catch ( IOException e ) {
throw new kelondroException ( filename , " io-error while rot " ) ;
}
2005-11-15 02:20:05 +01:00
if ( nextNode = = null ) throw new kelondroException ( filename , " nodeIterator.next: no more entries available " ) ;
2006-03-17 15:20:04 +01:00
if ( ( count > size ( ) ) & & ( ! ( rot ) ) ) throw new kelondroException ( filename , " nodeIterator.next: internal loopback; database corrupted " ) ;
2007-12-27 18:56:59 +01:00
CacheNode ret = nextNode ;
2005-05-08 16:42:09 +02:00
// middle-case
try {
int childtype = ( up ) ? rightchild : leftchild ;
2007-08-03 13:44:58 +02:00
kelondroHandle childHandle = nextNode . getOHHandle ( childtype ) ;
2005-05-08 16:42:09 +02:00
if ( childHandle ! = null ) {
//System.out.println("go to other leg, stack size=" + nodeStack.size());
// we have walked one leg of the tree; now go to the other one: step down to next child
2007-12-27 18:56:59 +01:00
HashSet < kelondroHandle > visitedNodeHandles = new HashSet < kelondroHandle > ( ) ; // to detect loops
2005-05-08 16:42:09 +02:00
nodeStack . addLast ( new Object [ ] { nextNode , new Integer ( childtype ) } ) ;
2007-08-03 13:44:58 +02:00
nextNode = new CacheNode ( childHandle , nextNode , childtype , false ) ;
2005-05-08 16:42:09 +02:00
childtype = ( up ) ? leftchild : rightchild ;
2005-09-15 22:45:51 +02:00
while ( ( childHandle = nextNode . getOHHandle ( childtype ) ) ! = null ) {
2005-11-15 02:20:05 +01:00
if ( visitedNodeHandles . contains ( childHandle ) ) {
// try to repair the nextNode
nextNode . setOHHandle ( childtype , null ) ;
2007-08-03 13:44:58 +02:00
nextNode . commit ( ) ;
2005-11-15 02:20:05 +01:00
logWarning ( " nodeIterator.next: internal loopback; fixed loop and try to go on " ) ;
break ;
}
visitedNodeHandles . add ( childHandle ) ;
2005-05-08 16:42:09 +02:00
try {
nodeStack . addLast ( new Object [ ] { nextNode , new Integer ( childtype ) } ) ;
2007-08-03 13:44:58 +02:00
nextNode = new CacheNode ( childHandle , nextNode , childtype , false ) ;
2005-05-08 16:42:09 +02:00
} catch ( IllegalArgumentException e ) {
// return what we have
nodeStack . removeLast ( ) ;
return ret ;
}
}
// thats it: we are at a place where we can't go further
// nextNode is correct
} else {
//System.out.println("go up");
// we have walked along both legs of the child-trees.
// Now step up.
if ( nodeStack . size ( ) = = 0 ) {
nextNode = null ;
} else {
Object [ ] stacktop ;
2007-08-03 13:44:58 +02:00
CacheNode parentNode = null ;
2005-05-08 16:42:09 +02:00
int parentpointer = ( up ) ? rightchild : leftchild ;
while ( ( nodeStack . size ( ) ! = 0 ) & & ( parentpointer = = ( ( up ) ? rightchild : leftchild ) ) ) {
//System.out.println("step up");
// go on, walk up further
stacktop = ( Object [ ] ) nodeStack . removeLast ( ) ; // top of stack: Node/parentpointer pair
2007-08-03 13:44:58 +02:00
parentNode = ( CacheNode ) stacktop [ 0 ] ;
2005-05-08 16:42:09 +02:00
parentpointer = ( ( Integer ) stacktop [ 1 ] ) . intValue ( ) ;
}
if ( ( nodeStack . size ( ) = = 0 ) & & ( parentpointer = = ( ( up ) ? rightchild : leftchild ) ) ) {
nextNode = null ;
} else {
2005-12-06 22:21:14 +01:00
nextNode = parentNode ;
2005-05-08 16:42:09 +02:00
}
}
}
} catch ( IOException e ) {
nextNode = null ;
}
return ret ;
}
public void remove ( ) {
2006-03-18 01:21:34 +01:00
throw new java . lang . UnsupportedOperationException ( " kelondroTree: remove in kelondro node iterator not yet supported " ) ;
2006-03-17 15:20:04 +01:00
}
2005-05-08 16:42:09 +02:00
}
2007-12-28 04:39:36 +01:00
public TreeMap < String , kelondroRow . Entry > rowMap ( boolean up , byte [ ] firstKey , boolean including , int count ) throws IOException {
2006-03-18 00:43:24 +01:00
// returns an ordered map of keys/row relations; key objects are of type String, value objects are of type byte[][]
2008-01-11 01:12:01 +01:00
kelondroByteOrder setOrder = ( kelondroByteOrder ) row ( ) . objectOrder . clone ( ) ;
2006-03-18 00:43:24 +01:00
setOrder . direction ( up ) ;
setOrder . rotate ( firstKey ) ;
2008-01-11 01:12:01 +01:00
TreeMap < String , kelondroRow . Entry > rows = new TreeMap < String , kelondroRow . Entry > ( this . loopDetectionOrder ) ;
2007-12-27 18:56:59 +01:00
CacheNode n ;
2006-07-24 00:39:41 +02:00
String key ;
2006-03-18 00:43:24 +01:00
synchronized ( this ) {
2007-12-27 18:56:59 +01:00
Iterator < CacheNode > i = ( firstKey = = null ) ? new nodeIterator ( up , false ) : new nodeIterator ( up , false , firstKey , including ) ;
2006-03-18 00:43:24 +01:00
while ( ( rows . size ( ) < count ) & & ( i . hasNext ( ) ) ) {
2007-12-27 18:56:59 +01:00
n = i . next ( ) ;
2006-07-24 00:39:41 +02:00
if ( n = = null ) return rows ;
key = new String ( n . getKey ( ) ) ;
2007-12-28 04:39:36 +01:00
if ( rows . put ( key , row ( ) . newEntry ( n . getValueRow ( ) ) ) ! = null ) return rows ; // protection against loops
2006-03-18 00:43:24 +01:00
}
}
return rows ;
}
2007-12-28 04:39:36 +01:00
public TreeSet < String > keySet ( boolean up , boolean rotating , byte [ ] firstKey , boolean including , int count ) throws IOException {
2006-03-18 00:43:24 +01:00
// returns an ordered set of keys; objects are of type String
2008-01-11 01:12:01 +01:00
kelondroByteOrder setOrder = ( kelondroByteOrder ) row ( ) . objectOrder . clone ( ) ;
2006-03-18 00:43:24 +01:00
setOrder . direction ( up ) ;
setOrder . rotate ( firstKey ) ;
2008-01-11 01:12:01 +01:00
TreeSet < String > set = new TreeSet < String > ( this . loopDetectionOrder ) ;
2007-08-03 13:44:58 +02:00
kelondroNode n ;
2006-03-18 00:43:24 +01:00
synchronized ( this ) {
2007-12-27 18:56:59 +01:00
Iterator < CacheNode > i = ( firstKey = = null ) ? new nodeIterator ( up , rotating ) : new nodeIterator ( up , rotating , firstKey , including ) ;
2006-03-18 00:43:24 +01:00
while ( ( set . size ( ) < count ) & & ( i . hasNext ( ) ) ) {
2007-08-03 13:44:58 +02:00
n = ( kelondroNode ) i . next ( ) ;
2007-12-28 04:39:36 +01:00
if ( ( n ! = null ) & & ( n . getKey ( ) ! = null ) ) set . add ( new String ( n . getKey ( ) ) ) ;
2006-03-18 00:43:24 +01:00
}
}
return set ;
}
2007-12-27 18:56:59 +01:00
public kelondroCloneableIterator < kelondroRow . Entry > rows ( boolean up , byte [ ] firstKey ) throws IOException {
2006-03-18 00:43:24 +01:00
// iterates the rows of the Nodes
// enumerated objects are of type byte[][]
2005-04-07 21:19:42 +02:00
// iterates the elements in a sorted way.
2006-03-18 00:43:24 +01:00
// if iteration should start at smallest element, set firstKey to null
2007-03-08 17:15:40 +01:00
return new rowIterator ( up , firstKey , this . size ( ) ) ;
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
public class rowIterator implements kelondroCloneableIterator < kelondroRow . Entry > {
2005-04-07 21:19:42 +02:00
2006-03-18 00:43:24 +01:00
int chunkSize ;
2007-03-08 17:15:40 +01:00
boolean inc ;
2006-03-18 00:43:24 +01:00
long count ;
byte [ ] lastKey ;
2007-12-28 04:39:36 +01:00
TreeMap < String , kelondroRow . Entry > rowBuffer ;
Iterator < Map . Entry < String , kelondroRow . Entry > > bufferIterator ;
2007-03-08 17:15:40 +01:00
long guessedCountLimit ;
2005-04-07 21:19:42 +02:00
2007-03-08 17:15:40 +01:00
public rowIterator ( boolean up , byte [ ] firstKey , long guessedCountLimit ) throws IOException {
this . guessedCountLimit = guessedCountLimit ;
2006-03-18 00:43:24 +01:00
inc = up ;
count = 0 ;
lastKey = null ;
2006-03-19 02:27:33 +01:00
//System.out.println("*** rowIterator: " + filename + ": readAheadChunkSize = " + readAheadChunkSize + ", lastIteratorCount = " + lastIteratorCount);
readAheadChunkSize = Math . min ( 1000 , 3 + ( int ) ( ( 3 * readAheadChunkSize + lastIteratorCount ) / 4 ) ) ;
chunkSize = ( int ) Math . min ( readAheadChunkSize / 3 , guessedCountLimit ) ;
2007-03-08 23:07:17 +01:00
rowBuffer = rowMap ( inc , firstKey , true , chunkSize ) ;
2006-03-18 00:43:24 +01:00
bufferIterator = rowBuffer . entrySet ( ) . iterator ( ) ;
2006-03-19 02:27:33 +01:00
lastIteratorCount = 0 ;
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
public rowIterator clone ( Object secondStart ) {
2007-03-08 17:15:40 +01:00
try {
2007-03-08 23:07:17 +01:00
return new rowIterator ( inc , ( byte [ ] ) secondStart , guessedCountLimit ) ;
2007-03-08 17:15:40 +01:00
} catch ( IOException e ) {
return null ;
}
}
2005-04-07 21:19:42 +02:00
public boolean hasNext ( ) {
2007-03-08 17:15:40 +01:00
return ( ( bufferIterator ! = null ) & & ( bufferIterator . hasNext ( ) ) & & ( count < size ( ) ) ) ;
2005-04-07 21:19:42 +02:00
}
2007-12-27 18:56:59 +01:00
public kelondroRow . Entry next ( ) {
2006-03-18 00:43:24 +01:00
if ( ! ( bufferIterator . hasNext ( ) ) ) return null ;
2007-12-28 04:39:36 +01:00
Map . Entry < String , kelondroRow . Entry > entry = bufferIterator . next ( ) ;
lastKey = entry . getKey ( ) . getBytes ( ) ;
2006-03-18 00:43:24 +01:00
// check if this was the last entry in the rowBuffer
if ( ! ( bufferIterator . hasNext ( ) ) ) {
// assign next buffer chunk
try {
2007-12-28 04:39:36 +01:00
lastKey [ lastKey . length - 1 ] + + ; // ***BUG??? FIXME
2007-03-08 17:15:40 +01:00
rowBuffer = rowMap ( inc , lastKey , false , chunkSize ) ;
2006-03-18 00:43:24 +01:00
bufferIterator = rowBuffer . entrySet ( ) . iterator ( ) ;
} catch ( IOException e ) {
rowBuffer = null ;
bufferIterator = null ;
}
2005-04-07 21:19:42 +02:00
}
2006-03-18 00:43:24 +01:00
// return the row
count + + ;
2006-03-19 02:27:33 +01:00
lastIteratorCount + + ;
2006-03-18 00:43:24 +01:00
return entry . getValue ( ) ;
2005-04-07 21:19:42 +02:00
}
public void remove ( ) {
2006-03-18 00:43:24 +01:00
if ( lastKey ! = null ) try {
2007-10-22 17:26:47 +02:00
kelondroTree . this . remove ( lastKey , true ) ;
2006-03-18 00:43:24 +01:00
} catch ( IOException e ) {
// do nothing
}
2005-04-07 21:19:42 +02:00
}
}
2007-12-27 18:56:59 +01:00
public kelondroCloneableIterator < byte [ ] > keys ( boolean up , byte [ ] firstKey ) throws IOException {
return new keyIterator ( up , firstKey , this . size ( ) ) ;
}
public class keyIterator implements kelondroCloneableIterator < byte [ ] > {
int chunkSize ;
boolean inc ;
long count ;
byte [ ] lastKey ;
2007-12-28 04:39:36 +01:00
TreeSet < String > keyBuffer ;
Iterator < String > bufferIterator ;
2007-12-27 18:56:59 +01:00
long guessedCountLimit ;
public keyIterator ( boolean up , byte [ ] firstKey , long guessedCountLimit ) throws IOException {
this . guessedCountLimit = guessedCountLimit ;
inc = up ;
count = 0 ;
lastKey = null ;
//System.out.println("*** rowIterator: " + filename + ": readAheadChunkSize = " + readAheadChunkSize + ", lastIteratorCount = " + lastIteratorCount);
readAheadChunkSize = Math . min ( 1000 , 3 + ( int ) ( ( 3 * readAheadChunkSize + lastIteratorCount ) / 4 ) ) ;
chunkSize = ( int ) Math . min ( readAheadChunkSize / 3 , guessedCountLimit ) ;
keyBuffer = keySet ( inc , false , firstKey , true , chunkSize ) ;
bufferIterator = keyBuffer . iterator ( ) ;
lastIteratorCount = 0 ;
}
public keyIterator clone ( Object secondStart ) {
try {
return new keyIterator ( inc , ( byte [ ] ) secondStart , guessedCountLimit ) ;
} catch ( IOException e ) {
return null ;
}
}
public boolean hasNext ( ) {
return ( ( bufferIterator ! = null ) & & ( bufferIterator . hasNext ( ) ) & & ( count < size ( ) ) ) ;
}
public byte [ ] next ( ) {
if ( ! ( bufferIterator . hasNext ( ) ) ) return null ;
2007-12-28 04:39:36 +01:00
lastKey = bufferIterator . next ( ) . getBytes ( ) ;
2007-12-27 18:56:59 +01:00
// check if this was the last entry in the rowBuffer
if ( ! ( bufferIterator . hasNext ( ) ) ) {
// assign next buffer chunk
try {
2007-12-28 04:39:36 +01:00
lastKey [ lastKey . length - 1 ] + + ; // ***BUG??? FIXME
2007-12-27 18:56:59 +01:00
keyBuffer = keySet ( inc , false , lastKey , false , chunkSize ) ;
bufferIterator = keyBuffer . iterator ( ) ;
} catch ( IOException e ) {
keyBuffer = null ;
bufferIterator = null ;
}
}
// return the row
count + + ;
lastIteratorCount + + ;
return lastKey ;
}
public void remove ( ) {
if ( lastKey ! = null ) try {
kelondroTree . this . remove ( lastKey , true ) ;
} catch ( IOException e ) {
// do nothing
}
}
}
2005-04-27 23:09:40 +02:00
public int imp ( File file , String separator ) throws IOException {
2005-04-07 21:19:42 +02:00
// imports a value-separated file, returns number of records that have been read
RandomAccessFile f = new RandomAccessFile ( file , " r " ) ;
String s ;
StringTokenizer st ;
int recs = 0 ;
2006-06-02 14:45:57 +02:00
kelondroRow . Entry buffer = row ( ) . newEntry ( ) ;
2005-04-07 21:19:42 +02:00
int c ;
int line = 0 ;
while ( ( s = f . readLine ( ) ) ! = null ) {
s = s . trim ( ) ;
line + + ;
if ( ( s . length ( ) > 0 ) & & ( ! ( s . startsWith ( " # " ) ) ) ) {
st = new StringTokenizer ( s , separator ) ;
// buffer the entry
c = 0 ;
2006-06-02 14:45:57 +02:00
while ( ( c < row ( ) . columns ( ) ) & & ( st . hasMoreTokens ( ) ) ) {
buffer . setCol ( c + + , st . nextToken ( ) . trim ( ) . getBytes ( ) ) ;
2005-04-07 21:19:42 +02:00
}
2006-06-02 14:45:57 +02:00
if ( ( st . hasMoreTokens ( ) ) | | ( c ! = row ( ) . columns ( ) ) ) {
2005-04-07 21:19:42 +02:00
System . err . println ( " inapropriate number of entries in line " + line ) ;
} else {
put ( buffer ) ;
recs + + ;
}
}
}
return recs ;
}
public synchronized int height ( ) {
try {
2007-08-03 13:44:58 +02:00
kelondroHandle h = getHandle ( root ) ;
2005-04-07 21:19:42 +02:00
if ( h = = null ) return 0 ;
2007-08-03 13:44:58 +02:00
return height ( new CacheNode ( h , null , 0 , false ) ) ;
2005-04-07 21:19:42 +02:00
} catch ( IOException e ) {
return 0 ;
}
}
2007-08-03 13:44:58 +02:00
private int height ( CacheNode node ) throws IOException {
2005-04-07 21:19:42 +02:00
if ( node = = null ) return 0 ;
2007-08-03 13:44:58 +02:00
kelondroHandle h = node . getOHHandle ( leftchild ) ;
int hl = ( h = = null ) ? 0 : height ( new CacheNode ( h , node , leftchild , false ) ) ;
2005-09-15 22:45:51 +02:00
h = node . getOHHandle ( rightchild ) ;
2007-08-03 13:44:58 +02:00
int hr = ( h = = null ) ? 0 : height ( new CacheNode ( h , node , rightchild , false ) ) ;
2006-08-16 21:49:31 +02:00
if ( hl > hr ) return hl + 1 ;
return hr + 1 ;
2005-04-07 21:19:42 +02:00
}
2007-02-20 09:35:51 +01:00
2005-04-07 21:19:42 +02:00
public void print ( ) throws IOException {
2007-08-03 13:44:58 +02:00
super . print ( ) ;
2005-04-07 21:19:42 +02:00
int height = height ( ) ;
System . out . println ( " HEIGHT = " + height ) ;
2007-12-27 18:56:59 +01:00
Vector < kelondroHandle > thisline = new Vector < kelondroHandle > ( ) ;
2005-04-07 21:19:42 +02:00
thisline . add ( getHandle ( root ) ) ;
2007-12-27 18:56:59 +01:00
Vector < kelondroHandle > nextline ;
2007-08-03 13:44:58 +02:00
kelondroHandle handle ;
kelondroNode node ;
2006-06-02 14:45:57 +02:00
int linelength , width = ( 1 < < ( height - 1 ) ) * ( row ( ) . width ( 0 ) + 1 ) ;
2005-04-07 21:19:42 +02:00
String key ;
for ( int h = 1 ; h < height ; h + + ) {
linelength = width / ( thisline . size ( ) * 2 ) ;
2007-12-27 18:56:59 +01:00
nextline = new Vector < kelondroHandle > ( ) ;
2005-04-07 21:19:42 +02:00
for ( int i = 0 ; i < thisline . size ( ) ; i + + ) {
2007-08-03 13:44:58 +02:00
handle = ( kelondroHandle ) thisline . elementAt ( i ) ;
2005-04-07 21:19:42 +02:00
if ( handle = = null ) {
node = null ;
key = " [..] " ;
} else {
2007-08-03 13:44:58 +02:00
node = new CacheNode ( handle , null , 0 , false ) ;
2005-04-07 21:19:42 +02:00
if ( node = = null ) key = " NULL " ; else key = new String ( node . getKey ( ) ) ;
}
System . out . print ( key ) ;
for ( int j = 0 ; j < ( linelength - key . length ( ) ) ; j + + ) System . out . print ( " - " ) ;
System . out . print ( " + " ) ;
for ( int j = 0 ; j < ( linelength - 1 ) ; j + + ) System . out . print ( " " ) ;
if ( node = = null ) {
nextline . add ( null ) ;
nextline . add ( null ) ;
} else {
2005-09-15 22:45:51 +02:00
nextline . add ( node . getOHHandle ( leftchild ) ) ;
nextline . add ( node . getOHHandle ( rightchild ) ) ;
2005-04-07 21:19:42 +02:00
}
}
System . out . println ( ) ;
for ( int i = 0 ; i < thisline . size ( ) ; i + + ) {
System . out . print ( " | " ) ;
for ( int j = 0 ; j < ( linelength - 1 ) ; j + + ) System . out . print ( " " ) ;
System . out . print ( " | " ) ;
for ( int j = 0 ; j < ( linelength - 1 ) ; j + + ) System . out . print ( " " ) ;
}
System . out . println ( ) ;
thisline = nextline ;
nextline = null ;
}
// now print last line
if ( ( thisline ! = null ) & & ( width > = 0 ) ) {
linelength = width / thisline . size ( ) ;
for ( int i = 0 ; i < thisline . size ( ) ; i + + ) {
2007-08-03 13:44:58 +02:00
handle = ( kelondroHandle ) thisline . elementAt ( i ) ;
2005-04-07 21:19:42 +02:00
if ( handle = = null ) {
node = null ;
key = " NULL " ;
} else {
2007-08-03 13:44:58 +02:00
node = new CacheNode ( handle , null , 0 , false ) ;
2005-04-07 21:19:42 +02:00
if ( node = = null ) key = " NULL " ; else key = new String ( node . getKey ( ) ) ;
}
System . out . print ( key ) ;
for ( int j = 0 ; j < ( linelength - key . length ( ) ) ; j + + ) System . out . print ( " " ) ;
}
}
System . out . println ( ) ;
}
2006-03-17 19:10:48 +01:00
public static void cmd ( String [ ] args ) {
2005-04-07 21:19:42 +02:00
System . out . print ( " kelondroTree " ) ;
for ( int i = 0 ; i < args . length ; i + + ) System . out . print ( args [ i ] + " " ) ;
System . out . println ( " " ) ;
byte [ ] ret = null ;
try {
if ( ( args . length > 4 ) | | ( args . length < 1 ) ) {
System . err . println ( " usage: kelondroTree -c|-u|-v|-g|-d|-i|-s [file]|[key [value]] <db-file> " ) ;
System . err . println ( " ( create, update, view, get, delete, imp, shell) " ) ;
System . exit ( 0 ) ;
} else if ( args . length = = 1 ) {
if ( args [ 0 ] . equals ( " -t " ) ) {
// test script
File testFile = new File ( " test.db " ) ;
while ( testFile . exists ( ) ) testFile . delete ( ) ;
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( testFile , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
byte [ ] dummy = " " . getBytes ( ) ;
fm . put ( " abc0 " . getBytes ( ) , dummy ) ; fm . put ( " bcd0 " . getBytes ( ) , dummy ) ;
fm . put ( " def0 " . getBytes ( ) , dummy ) ; fm . put ( " bab0 " . getBytes ( ) , dummy ) ;
fm . put ( " abc1 " . getBytes ( ) , dummy ) ; fm . put ( " bcd1 " . getBytes ( ) , dummy ) ;
fm . put ( " def1 " . getBytes ( ) , dummy ) ; fm . put ( " bab1 " . getBytes ( ) , dummy ) ;
fm . put ( " abc2 " . getBytes ( ) , dummy ) ; fm . put ( " bcd2 " . getBytes ( ) , dummy ) ;
fm . put ( " def2 " . getBytes ( ) , dummy ) ; fm . put ( " bab2 " . getBytes ( ) , dummy ) ;
fm . put ( " abc3 " . getBytes ( ) , dummy ) ; fm . put ( " bcd3 " . getBytes ( ) , dummy ) ;
fm . put ( " def3 " . getBytes ( ) , dummy ) ; fm . put ( " bab3 " . getBytes ( ) , dummy ) ;
2006-03-17 19:10:48 +01:00
fm . print ( ) ;
2007-10-22 17:26:47 +02:00
fm . remove ( " def1 " . getBytes ( ) , true ) ; fm . remove ( " bab1 " . getBytes ( ) , true ) ;
fm . remove ( " abc2 " . getBytes ( ) , true ) ; fm . remove ( " bcd2 " . getBytes ( ) , true ) ;
fm . remove ( " def2 " . getBytes ( ) , true ) ; fm . remove ( " bab2 " . getBytes ( ) , true ) ;
2005-04-07 21:19:42 +02:00
fm . put ( " def1 " . getBytes ( ) , dummy ) ; fm . put ( " bab1 " . getBytes ( ) , dummy ) ;
fm . put ( " abc2 " . getBytes ( ) , dummy ) ; fm . put ( " bcd2 " . getBytes ( ) , dummy ) ;
fm . put ( " def2 " . getBytes ( ) , dummy ) ; fm . put ( " bab2 " . getBytes ( ) , dummy ) ;
fm . print ( ) ;
fm . close ( ) ;
ret = null ;
}
} else if ( args . length = = 2 ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
if ( args [ 0 ] . equals ( " -v " ) ) {
fm . print ( ) ;
ret = null ;
}
fm . close ( ) ;
} else if ( args . length = = 3 ) {
if ( args [ 0 ] . equals ( " -d " ) ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2007-10-22 17:26:47 +02:00
fm . remove ( args [ 2 ] . getBytes ( ) , true ) ;
2005-04-07 21:19:42 +02:00
fm . close ( ) ;
} else if ( args [ 0 ] . equals ( " -i " ) ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
int i = fm . imp ( new File ( args [ 1 ] ) , " ; " ) ;
fm . close ( ) ;
ret = ( i + " records imported " ) . getBytes ( ) ;
} else if ( args [ 0 ] . equals ( " -s " ) ) {
String db = args [ 2 ] ;
2005-08-30 08:45:49 +02:00
BufferedReader f = null ;
try {
f = new BufferedReader ( new FileReader ( args [ 1 ] ) ) ;
String m ;
while ( true ) {
m = f . readLine ( ) ;
if ( m = = null ) break ;
if ( ( m . length ( ) > 1 ) & & ( ! m . startsWith ( " # " ) ) ) {
m = m + " " + db ;
cmd ( line2args ( m ) ) ;
}
}
ret = null ;
} finally {
if ( f ! = null ) try { f . close ( ) ; } catch ( Exception e ) { }
}
2005-04-07 21:19:42 +02:00
} else if ( args [ 0 ] . equals ( " -g " ) ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2006-06-01 01:31:46 +02:00
kelondroRow . Entry ret2 = fm . get ( args [ 2 ] . getBytes ( ) ) ;
ret = ( ( ret2 = = null ) ? null : ret2 . getColBytes ( 1 ) ) ;
2005-04-07 21:19:42 +02:00
fm . close ( ) ;
} else if ( args [ 0 ] . equals ( " -n " ) ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
//byte[][] keys = fm.getSequentialKeys(args[2].getBytes(), 500, true);
2007-12-27 18:56:59 +01:00
Iterator < kelondroRow . Entry > rowIt = fm . rows ( true , ( args [ 2 ] . length ( ) = = 0 ) ? null : args [ 2 ] . getBytes ( ) ) ;
Vector < String > v = new Vector < String > ( ) ;
while ( rowIt . hasNext ( ) ) v . add ( rowIt . next ( ) . getColString ( 0 , null ) ) ;
2005-04-07 21:19:42 +02:00
ret = v . toString ( ) . getBytes ( ) ;
fm . close ( ) ;
}
} else if ( args . length = = 4 ) {
if ( args [ 0 ] . equals ( " -c " ) ) {
// create <keylen> <valuelen> <filename>
File f = new File ( args [ 3 ] ) ;
if ( f . exists ( ) ) f . delete ( ) ;
2006-12-06 04:02:57 +01:00
kelondroRow lens = new kelondroRow ( " byte[] key- " + Integer . parseInt ( args [ 1 ] ) + " , byte[] value- " + Integer . parseInt ( args [ 2 ] ) , kelondroNaturalOrder . naturalOrder , 0 ) ;
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( f , true , 10 , lens ) ;
2005-04-07 21:19:42 +02:00
fm . close ( ) ;
} else if ( args [ 0 ] . equals ( " -u " ) ) {
2007-03-06 23:43:32 +01:00
kelondroTree fm = new kelondroTree ( new File ( args [ 1 ] ) , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
ret = fm . put ( args [ 1 ] . getBytes ( ) , args [ 2 ] . getBytes ( ) ) ;
fm . close ( ) ;
}
}
if ( ret = = null )
System . out . println ( " NULL " ) ;
else
System . out . println ( new String ( ret ) ) ;
} catch ( Exception e ) {
e . printStackTrace ( ) ;
}
}
public static void main ( String [ ] args ) {
2006-03-17 19:10:48 +01:00
//cmd(args);
2006-06-18 14:04:41 +02:00
//iterationtest();
2005-11-14 01:23:20 +01:00
//bigtest(Integer.parseInt(args[0]));
2007-02-20 09:35:51 +01:00
randomtest ( Integer . parseInt ( args [ 0 ] ) ) ;
//smalltest();
2005-04-07 21:19:42 +02:00
}
public static String [ ] permutations ( int letters ) {
String p = " " ;
for ( int i = 0 ; i < letters ; i + + ) p = p + ( ( char ) ( ( ( int ) 'A' ) + i ) ) ;
return permutations ( p ) ;
}
public static String [ ] permutations ( String source ) {
if ( source . length ( ) = = 0 ) return new String [ 0 ] ;
if ( source . length ( ) = = 1 ) return new String [ ] { source } ;
char c = source . charAt ( 0 ) ;
String [ ] recres = permutations ( source . substring ( 1 ) ) ;
String [ ] result = new String [ source . length ( ) * recres . length ] ;
for ( int perm = 0 ; perm < recres . length ; perm + + ) {
result [ perm * source . length ( ) ] = c + recres [ perm ] ;
for ( int pos = 1 ; pos < source . length ( ) - 1 ; pos + + ) {
result [ perm * source . length ( ) + pos ] = recres [ perm ] . substring ( 0 , pos ) + c + recres [ perm ] . substring ( pos ) ;
}
result [ perm * source . length ( ) + source . length ( ) - 1 ] = recres [ perm ] + c ;
}
return result ;
}
public static byte [ ] testWord ( char c ) {
return new byte [ ] { ( byte ) c , 32 , 32 , 32 } ;
}
public static void randomtest ( int elements ) {
System . out . println ( " random " + elements + " : " ) ;
String s = " ABCDEFGHIJKLMNOPQRSTUVWXYZ " . substring ( 0 , elements ) ;
String t , d ;
char c ;
2005-04-27 23:09:40 +02:00
kelondroTree tt = null ;
2005-04-07 21:19:42 +02:00
File testFile = new File ( " test.db " ) ;
byte [ ] b ;
try {
int steps = 0 ;
while ( true ) {
if ( testFile . exists ( ) ) testFile . delete ( ) ;
2007-03-06 23:43:32 +01:00
tt = new kelondroTree ( testFile , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
steps = 10 + ( ( int ) System . currentTimeMillis ( ) % 7 ) * ( ( ( int ) System . currentTimeMillis ( ) + 17 ) % 11 ) ;
t = s ;
d = " " ;
System . out . println ( " NEW SESSION " ) ;
for ( int i = 0 ; i < steps ; i + + ) {
if ( ( d . length ( ) < 3 ) | | ( ( t . length ( ) > 0 ) & & ( ( ( int ) System . currentTimeMillis ( ) % 7 ) < 2 ) ) ) {
// add one
2005-12-07 00:51:29 +01:00
c = t . charAt ( ( int ) ( System . currentTimeMillis ( ) % t . length ( ) ) ) ;
2005-04-07 21:19:42 +02:00
b = testWord ( c ) ;
tt . put ( b , b ) ;
d = d + c ;
t = t . substring ( 0 , t . indexOf ( c ) ) + t . substring ( t . indexOf ( c ) + 1 ) ;
System . out . println ( " added " + new String ( b ) ) ;
} else {
// delete one
2005-12-07 00:51:29 +01:00
c = d . charAt ( ( int ) ( System . currentTimeMillis ( ) % d . length ( ) ) ) ;
2005-04-07 21:19:42 +02:00
b = testWord ( c ) ;
2007-10-22 17:26:47 +02:00
tt . remove ( b , true ) ;
2005-04-07 21:19:42 +02:00
d = d . substring ( 0 , d . indexOf ( c ) ) + d . substring ( d . indexOf ( c ) + 1 ) ;
t = t + c ;
System . out . println ( " removed " + new String ( b ) ) ;
}
2005-09-14 12:10:49 +02:00
//tt.printCache();
2005-04-27 23:09:40 +02:00
//tt.print();
2005-09-14 12:10:49 +02:00
2005-04-07 21:19:42 +02:00
if ( countElements ( tt ) ! = tt . size ( ) ) {
2005-09-14 12:10:49 +02:00
System . out . println ( " wrong size for this table: " ) ;
2005-04-07 21:19:42 +02:00
tt . print ( ) ;
}
2005-09-14 12:10:49 +02:00
2005-04-07 21:19:42 +02:00
// check all words within
for ( int j = 0 ; j < d . length ( ) ; j + + ) {
if ( tt . get ( testWord ( d . charAt ( j ) ) ) = = null ) {
2005-09-14 12:10:49 +02:00
System . out . println ( " missing entry " + d . charAt ( j ) + " in this table: " ) ;
2005-04-07 21:19:42 +02:00
tt . print ( ) ;
}
}
// check all words outside
for ( int j = 0 ; j < t . length ( ) ; j + + ) {
if ( tt . get ( testWord ( t . charAt ( j ) ) ) ! = null ) {
2005-09-14 12:10:49 +02:00
System . out . println ( " superfluous entry " + t . charAt ( j ) + " in this table: " ) ;
2005-04-07 21:19:42 +02:00
tt . print ( ) ;
}
}
if ( tt . get ( testWord ( 'z' ) ) ! = null ) {
2005-09-14 12:10:49 +02:00
System . out . println ( " superfluous entry z in this table: " ) ;
2005-04-07 21:19:42 +02:00
tt . print ( ) ;
}
2005-09-14 12:10:49 +02:00
2005-04-07 21:19:42 +02:00
}
2005-09-14 12:10:49 +02:00
//tt.print();
2005-04-07 21:19:42 +02:00
tt . close ( ) ;
}
} catch ( Exception e ) {
e . printStackTrace ( ) ;
2005-04-27 23:09:40 +02:00
try { tt . print ( ) ; } catch ( IOException ee ) { }
2005-04-07 21:19:42 +02:00
System . out . println ( " TERMINATED " ) ;
}
}
public static void smalltest ( ) {
File f = new File ( " test.db " ) ;
if ( f . exists ( ) ) f . delete ( ) ;
try {
2007-03-06 23:43:32 +01:00
kelondroTree tt = new kelondroTree ( f , true , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-04-07 21:19:42 +02:00
byte [ ] b ;
2005-11-14 01:23:20 +01:00
b = testWord ( 'B' ) ; tt . put ( b , b ) ; //tt.print();
b = testWord ( 'C' ) ; tt . put ( b , b ) ; //tt.print();
b = testWord ( 'D' ) ; tt . put ( b , b ) ; //tt.print();
b = testWord ( 'A' ) ; tt . put ( b , b ) ; //tt.print();
2007-10-22 17:26:47 +02:00
b = testWord ( 'D' ) ; tt . remove ( b , true ) ; //tt.print();
b = testWord ( 'B' ) ; tt . remove ( b , true ) ; //tt.print();
2005-11-14 01:23:20 +01:00
b = testWord ( 'B' ) ; tt . put ( b , b ) ; //tt.print();
b = testWord ( 'D' ) ; tt . put ( b , b ) ;
b = testWord ( 'E' ) ; tt . put ( b , b ) ;
b = testWord ( 'F' ) ; tt . put ( b , b ) ;
b = testWord ( 'G' ) ; tt . put ( b , b ) ;
2006-06-18 14:04:41 +02:00
b = testWord ( 'H' ) ; tt . put ( b , b ) ;
2005-11-14 01:23:20 +01:00
b = testWord ( 'I' ) ; tt . put ( b , b ) ;
b = testWord ( 'J' ) ; tt . put ( b , b ) ;
b = testWord ( 'K' ) ; tt . put ( b , b ) ;
b = testWord ( 'L' ) ; tt . put ( b , b ) ;
int c = countElements ( tt ) ;
System . out . println ( " elements: " + c ) ;
2007-12-27 18:56:59 +01:00
Iterator < kelondroRow . Entry > i = tt . rows ( true , testWord ( 'G' ) ) ;
2005-11-14 01:23:20 +01:00
for ( int j = 0 ; j < c ; j + + ) {
2006-06-18 14:04:41 +02:00
System . out . println ( " Row " + j + " : " + new String ( ( ( kelondroRow . Entry ) i . next ( ) ) . getColBytes ( 0 ) ) ) ;
2005-11-14 01:23:20 +01:00
}
2005-09-14 12:10:49 +02:00
System . out . println ( " TERMINATED " ) ;
2005-04-07 21:19:42 +02:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
}
2006-07-26 13:21:51 +02:00
/ *
2006-03-17 19:10:48 +01:00
public static void iterationtest ( ) {
File f = new File ( " test.db " ) ;
if ( f . exists ( ) ) f . delete ( ) ;
try {
2006-07-04 01:57:33 +02:00
kelondroTree tt = new kelondroTree ( f , 0 , 0 , 10 , 4 , 4 , true ) ;
2006-03-17 19:10:48 +01:00
byte [ ] b ;
for ( int i = 0 ; i < 100 ; i + + ) {
b = ( " T " + i ) . getBytes ( ) ; tt . put ( b , b ) ;
}
2006-03-18 00:43:24 +01:00
Iterator i = tt . keys ( true , false , null ) ;
2006-03-17 19:10:48 +01:00
while ( i . hasNext ( ) ) System . out . print ( ( String ) i . next ( ) + " , " ) ;
System . out . println ( ) ;
i = tt . keys ( true , false , " T80 " . getBytes ( ) ) ;
while ( i . hasNext ( ) ) System . out . print ( ( String ) i . next ( ) + " , " ) ;
System . out . println ( ) ;
i = tt . keys ( true , true , " T80 " . getBytes ( ) ) ;
for ( int j = 0 ; j < 40 ; j + + ) System . out . print ( ( String ) i . next ( ) + " , " ) ;
System . out . println ( ) ;
i = tt . keys ( false , true , " T20 " . getBytes ( ) ) ;
for ( int j = 0 ; j < 40 ; j + + ) System . out . print ( ( String ) i . next ( ) + " , " ) ;
System . out . println ( ) ;
tt . close ( ) ;
} catch ( IOException e ) {
e . printStackTrace ( ) ;
}
}
2006-07-26 13:21:51 +02:00
* /
2006-03-17 19:10:48 +01:00
2005-09-14 12:10:49 +02:00
public static kelondroTree testTree ( File f , String testentities ) throws IOException {
if ( f . exists ( ) ) f . delete ( ) ;
2007-03-06 23:43:32 +01:00
kelondroTree tt = new kelondroTree ( f , false , 10 , new kelondroRow ( " byte[] a-4, byte[] b-4 " , kelondroNaturalOrder . naturalOrder , 0 ) ) ;
2005-09-14 12:10:49 +02:00
byte [ ] b ;
for ( int i = 0 ; i < testentities . length ( ) ; i + + ) {
b = testWord ( testentities . charAt ( i ) ) ;
tt . put ( b , b ) ;
}
return tt ;
}
public static void bigtest ( int elements ) {
System . out . println ( " starting big test with " + elements + " elements: " ) ;
long start = System . currentTimeMillis ( ) ;
2005-04-07 21:19:42 +02:00
String [ ] s = permutations ( elements ) ;
kelondroTree tt ;
File testFile = new File ( " test.db " ) ;
try {
for ( int i = 0 ; i < s . length ; i + + ) {
2005-09-14 12:10:49 +02:00
System . out . println ( " *** probing tree " + i + " for permutation " + s [ i ] ) ;
2005-04-07 21:19:42 +02:00
// generate tree and delete elements
tt = testTree ( testFile , s [ i ] ) ;
//tt.print();
if ( countElements ( tt ) ! = tt . size ( ) ) {
System . out . println ( " wrong size for " + s [ i ] ) ;
tt . print ( ) ;
}
tt . close ( ) ;
for ( int j = 0 ; j < s . length ; j + + ) {
tt = testTree ( testFile , s [ i ] ) ;
//tt.print();
// delete by permutation j
for ( int elt = 0 ; elt < s [ j ] . length ( ) ; elt + + ) {
2007-10-22 17:26:47 +02:00
tt . remove ( testWord ( s [ j ] . charAt ( elt ) ) , true ) ;
2005-04-07 21:19:42 +02:00
//tt.print();
if ( countElements ( tt ) ! = tt . size ( ) ) {
System . out . println ( " ERROR! wrong size for probe tree " + s [ i ] + " ; probe delete " + s [ j ] + " ; position " + elt ) ;
tt . print ( ) ;
}
}
// add another one
//tt.print();
/ *
b = testWord ( '0' ) ; tt . put ( b , b ) ;
b = testWord ( 'z' ) ; tt . put ( b , b ) ;
b = testWord ( 'G' ) ; tt . put ( b , b ) ;
b = testWord ( 't' ) ; tt . put ( b , b ) ;
if ( countElements ( tt ) ! = tt . size ( ) ) {
System . out . println ( " ERROR! wrong size for probe tree " + s [ i ] + " ; probe delete " + s [ j ] + " ; final add " ) ;
tt . print ( ) ;
}
tt . print ( ) ;
* /
// close this
tt . close ( ) ;
}
}
2005-09-14 12:10:49 +02:00
System . out . println ( " FINISHED test after " + ( ( System . currentTimeMillis ( ) - start ) / 1000 ) + " seconds. " ) ;
2005-04-07 21:19:42 +02:00
} catch ( Exception e ) {
e . printStackTrace ( ) ;
System . out . println ( " TERMINATED " ) ;
}
}
2008-01-17 13:12:52 +01:00
public static int countElements ( kelondroIndex t ) {
2005-04-07 21:19:42 +02:00
int count = 0 ;
2006-03-17 21:52:43 +01:00
try {
2007-12-27 18:56:59 +01:00
Iterator < kelondroRow . Entry > iter = t . rows ( true , null ) ;
2006-06-18 14:04:41 +02:00
kelondroRow . Entry row ;
2006-03-17 21:52:43 +01:00
while ( iter . hasNext ( ) ) {
count + + ;
2007-12-27 18:56:59 +01:00
row = iter . next ( ) ;
2006-03-17 21:52:43 +01:00
if ( row = = null ) System . out . println ( " ERROR! null element found " ) ;
// else System.out.println("counted element: " + new
// String(n.getKey()));
}
} catch ( IOException e ) {
2005-09-14 12:10:49 +02:00
}
2005-04-07 21:19:42 +02:00
return count ;
}
2007-12-27 18:56:59 +01:00
2005-04-07 21:19:42 +02:00
}