//Node.java
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
package rtree;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
This class will contain one node at a time in memory.
This class along with FileHdr are the only two classes that handle the
rtree file. No other class handles the rtree file. Therefore be very careful
when modifying this class, you may corrupt the file in the process.
</p><b>CHANGELOG</b> See the projects/ChangeLog file
@author Prachuryya Barua
@version Node.NOT_DEFINED
*/
public class Node implements Cloneable //can be made abstract if leaf and non leaf required
{
/**max no. of entries in a node*/
// public final static int MAX = 169;//84;//101; //50;//testing 3
public final static int MAX = 40;//84;//101; //50;//testing 3
/**min. no. of entries in a node*/
// public final static int MIN= 84;//51; //25;//testing 2
public final static int MIN= 20;//51; //25;//testing 2
/**The size of the cache.<br>
Minimum cache size is 50% of total no. of elements (1lakh records has 597 nodes).
<br>Maximum cache size should be 70%, beyound that there may not be major improvements but the
overheads will increase.
<br>Eg: 1 lakh packed records - Total nodes in tree - 597: Height 3, 1 root,4 nonleaf, 129 leaf
and should have a cache size of 250(sufficient for normal casses) to 400(good for queries).
<br>Multiply the cache size by 4kbytes and you get the cache size in MBytes.
<br><b>These observations are for a packed tree only. An unpacked tree
needs a 100% buffer size - thererore don't use unpacked tree.</b>
<br>For unpacked tree
<br>1 lakh records - Total nodes in tree - 991( Height 3: 1 root, 10 NonLeaf, 981 Leaf
These observations shold hold good for query building as well. But it is no harm to give large caches
for query building.
*/
public final static int CACHE_SIZE = 250;
/**bytes*/
final static int NODE_HDR_SIZE = 20;//16;
/**2 kBytes - will include the file header and the stack*/
final static int FILE_HDR_SIZE = 4096;//2048;//1024;
/** 2 kByte*/
final static int NODE_SIZE = 4096;//2048;//1024;
/**2048-16=2032 then 2048-20=2028 NOW 4096-20=4076*/
final static int NODE_BODY_SIZE = 4076;//2028;//2032;//1008;
/**can be increased by increasing file header size*/
final static int FREE_LIST_LIMIT = 1020;//509;//5102k;//2541k;
/**So that I don't forget*/
final static int INTEGER_SIZE = 4;
/**So that I don't forget*/
final static int LONG_SIZE = 8;
/**node elements type - LEAF*/
public final static int LEAF_NODE = 1;
/**node elements type - NON LEAF*/
public final static int NONLEAF_NODE = 2;
/**node elements type - NONE*/
public final static int NOT_DEFINED = -999;
public final static long NOT_DEFINED_LONG = -999;
/**for thread which reads the tree*/
final static int READ = 0;
/**for threads which writes the reads*/
final static int WRITE = 1;
/**No threads reading or writing*/
final static int NONE = 2;
/**-------------local variables-----------------------*/
protected RandomAccessFile file;
protected String fileName;
protected boolean dirty = false;/*This flags keeps track of the changes made*/
/**will contain the index of the node in the file*/
protected long nodeIndex;
/**a flag to see whether a node is empty*/
//protected boolean isNodeEmpty;
protected boolean sorted;
/**will contain all the elements.*/
protected Element[] elements;
/**the file header and the free nodes stack*/
protected FileHdr fileHdr;
/**The cached Node MBR... no loops over the node Elements*/
protected Rect nodeMBR;//remove
/**-------------------Node header and body-----------------------*/
/**total no. of elements*/
protected int totalElements;
/**parent index - root's parent is not defined*/
protected long parent;
/**size of the element*/
protected int elementSize;
/**type of the elements in the node*/
protected int elementType;
/**---------------Element structure------------*/
/**
*minX
*minY
*maxX
*maxY
*pointer
*/
/**This is only for subclasses*/
protected Node(){}
/**for a new node
Remember if the file is new this constructor will overwrite the
<code>parent</code> parameter with 'NOT_DEFINED' as it will be the root node.
If the <code>prnt</code> parameter is given as NOT_DEFINED then it is understood
that a new root is required.The file will then have a new root.
*/
protected Node(RandomAccessFile file,String fileName, long prnt,int elmtType, FileHdr flHdr)
throws IOException, NodeWriteException
{
this.file = file;
fileHdr = flHdr;
//initialise local variables
this.fileName = fileName;
elements = new Element[MAX];
nodeMBR = new Rect();//remove
int size;//size of the element type
/* Though there is not much difference between LeafElement and NonLeafElement but we still use
the if condition as there may sometime in the future come a case where they differ.
But again then again to incorporate such change we must make MAX dynamic with the type of the element
we have.
*/
if(elmtType == NONLEAF_NODE)
size = NonLeafElement.sizeInBytes();
else
size = LeafElement.sizeInBytes();
try{
/*a new file with an empty root node*/
//if((file.length() <= (FILE_HDR_SIZE+2))){//no nodes written
if(fileHdr.getRootIndex() == NOT_DEFINED){//no nodes written
//writing the file header
fileHdr.writeFileHeader(1,0);
//local variables
nodeIndex = 0;//this is root - though empty
//isNodeEmpty = true;
//write node header
writeNodeHeader(fileHdr.rootIndex,0,NOT_DEFINED,size,elmtType);
}
/*for an existing file with a new node*/
else{
//see if any free node exists
try{
nodeIndex = fileHdr.pop();
}
catch(StackUnderflowException e){//else
nodeIndex = fileHdr.totalNodes++;//new node index
}
//local variables
//isNodeEmpty = true;
if(prnt == NOT_DEFINED)/*new Node is the root node.*/
fileHdr.writeFileHeader(fileHdr.totalNodes,nodeIndex);
else/*new node is any other node*/
fileHdr.writeFileHeader(fileHdr.totalNodes,fileHdr.rootIndex);
//write the node
writeNodeHeader(nodeIndex,0,prnt,size,elmtType);
}
return;
}
catch(IOException e){
throw new IOException("Node.Node(new) : " + e.getMessage());
}
}
/**
Reading existing nodes. But if this a new file then it will create the
file and make a new root node.
*/
protected Node(RandomAccessFile file,String fileName,long ndIndex,FileHdr flHdr)
throws FileNotFoundException,IOException,NodeReadException, NodeWriteException
{
//check whether file is new or old
//see whether the user must be specified or not if it is a new file
fileHdr = flHdr;
this.file = file;
this.fileName = fileName;
elements = new Element[MAX];
nodeMBR = new Rect();//remove
//a new file with an empty root node
//if(file.length() <= (FILE_HDR_SIZE+2)){//new file with no nodes
if(fileHdr.getRootIndex() == NOT_DEFINED){//no nodes written
try{
//write file header
fileHdr.writeFileHeader(1,0);
//local variables
nodeIndex = 0;//this is root - though empty
//isNodeEmpty = true;
//write node header
writeNodeHeader(fileHdr.rootIndex, 0, NOT_DEFINED, LeafElement.sizeInBytes(), LEAF_NODE);
}
catch(IOException e){
throw new IOException("Node.constructor : Can't write to fileHeader and/or node " + e.getMessage());
}
}
//if an old file with an existing node
else{
//if out of bond index
if((FILE_HDR_SIZE+(NODE_SIZE*ndIndex)) > file.length())
throw new NodeReadException("Node.Node.: nodeIndex is out of bound");
//update the local variable
this.nodeIndex = ndIndex;
//read all the values into local variables from the node
refreshNode();
}
}
/**
A stupid internal constructor for the clone method.
*/
protected Node(RandomAccessFile file,String fileName,long nodeIndex, boolean sorted,
Element[] elmts, FileHdr fileHdr, int totalElements,long parent,int elmtSize,
int elmtType, boolean dirty, Rect nodeMBR)//remove
{
try{
//Integer intVal;
this.file = file;
this.dirty = dirty;
this.fileName = new String(fileName.toCharArray());
this.nodeIndex = nodeIndex;
//this.isNodeEmpty = isNodeEmpty;
this.sorted = sorted;
this.nodeMBR = new Rect(nodeMBR);//remove
this.elements = new Element[elmts.length];
for(int i=0; i<elmts.length; i++){
if(elmts[i] != null){
if(elmtType == LEAF_NODE)
this.elements[i] = new LeafElement(new Rect(elmts[i].getRect()), elmts[i].getPtr());
else
this.elements[i] = new NonLeafElement(new Rect(elmts[i].getRect()), elmts[i].getPtr());
}//if
}//for
this.fileHdr = fileHdr;
this.totalElements = totalElements;
this.parent = parent;
this.elementSize = elmtSize;
this.elementType = elmtType;
}
catch(Exception e){
e.printStackTrace();
}
}
public Object clone()
{
return new Node(file,fileName,nodeIndex, sorted, elements, fileHdr,totalElements,
parent, elementSize, elementType, dirty, nodeMBR);//remove
}
/**
read an element from the hard disk. Not used any more
@deprecated In-fact it has never been used except in early development days.
*/
private Element readElement(long index) throws NodeReadException
{
if((index < 0)||(index > (MAX-1)) || (totalElements > index+1 ))
throw new NodeReadException("Node.readElement: Index value not correct");
try
{
int minX,minY,maxX,maxY;
//create a buffer
byte[] data = new byte[NODE_SIZE];
seekCurrNode();
file.read(data);
DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
//skip the header - check for error value
int skipValue = NODE_HDR_SIZE + (elementSize * (int)index);
if(ds.skipBytes(skipValue) != skipValue)
throw new NodeReadException("Can't read buffer: Header or index wrong");
//read the points
minX = ds.readInt();
minY = ds.readInt();
maxX = ds.readInt();
maxY = ds.readInt();
Rect Rectangle = new Rect(minX,minY,maxX,maxY);
if(elementType == LEAF_NODE){
long ptr = ds.readLong();
return(new LeafElement(Rectangle, ptr));
}
//if non leaf type then...
long nodePtr = ds.readLong();
return(new NonLeafElement(Rectangle, nodePtr));
}
catch(Exception e){
throw new NodeReadException("Node.readElement: " +e.getMessage());
}
}
/**
This method delets the element with the given index from the node.
It rewrites the node.
This method now also being used to write the whole node to the file.
@param index The element to delete. Give -1 if the whole node is to be flushed.
@param force Whether to force IO. As this method is also used to write the whole node, this was
required.
@return thengaa!
XXX : This is till not correct.
*/
public void deleteElement(int index, boolean force)
throws IllegalValueException, NodeWriteException
{
if((index > (totalElements-1)))
throw new IllegalValueException("Node.deleteElement: index out of bound");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
int j = -1;
try{
nodeMBR = new Rect();//remove
ByteArrayOutputStream bs = null;
DataOutputStream ds = null;
if(fileHdr.isWriteThr() || force){
bs = new ByteArrayOutputStream(NODE_SIZE);
ds = new DataOutputStream(bs);
if(index < 0)
ds.writeInt(totalElements);
else
ds.writeInt(totalElements - 1);
ds.writeLong(parent);
ds.writeInt(elementSize);
ds.writeInt(elementType);
}
for(int i=0; i<totalElements; i++){
if(i != index){
nodeMBR.expandToInclude(elements[i].getRect());//update the local variable as well - remove
if(fileHdr.isWriteThr() || force){//same condition of buffer policy again, we wanted the loop
ds.writeInt(elements[i].getRect().getMinX());
ds.writeInt(elements[i].getRect().getMinY());
ds.writeInt(elements[i].getRect().getMaxX());
ds.writeInt(elements[i].getRect().getMaxY());
ds.writeLong(elements[i].getPtr());//see [2]
}//if
}else
j = i;
}//for
if(fileHdr.isWriteThr() || force){
bs.flush();
ds.flush();
seekCurrNode();
file.write(bs.toByteArray());
setDirty(false);
}else{//if we do not write through
setDirty(true);
for(int i=0; i<totalElements; i++)
if(i != index)
nodeMBR.expandToInclude(elements[i].getRect());//update the local variable as well - remove
else
j = i;
}//else
}catch(Exception e){
e.printStackTrace();
throw new NodeWriteException("Node.deleteElement Can't delete element. Rtree may be corrupted.");
}
//update local variable
try{
if(j != -1){//do these things only if it an element was deleted
totalElements--;
if(totalElements>0)
System.arraycopy(elements,j+1,elements,j,(totalElements-j));
// else
// isNodeEmpty = true;
}
}catch(Exception e){
System.out.println("Node.deleteElement : Error while updating "
+"local variable...reading back from file..");
try{
refreshNode();
System.out.println("...successful");
}catch(IOException ex){
setDirty(true);
System.out.println("..node corrupted, rebuild tree ...quitting");
throw new NodeWriteException("Node.deleteElement : Can't delete element");
}
}
}
/**to add an element at the end
As elements are allocated to this node, each allocated
element's children node's parent are reset. This is simply because the
new node index(for <code>elmt</code>) would be different from the old
ones(if any).
<br>Note:-This again is for non leaf node only.
*/
public void insertElement(Element elmt)
throws NodeWriteException, NodeFullException
{
//check for space
if((totalElements == MAX))
throw new NodeFullException("Node.insertElement: Node full");
//check if it is an empty node or not
//if(!isNodeEmpty){//if not empty
if(totalElements > 0){//if not empty
if(elmt.getElementType() != elementType)
throw new NodeWriteException("Node.insertElement: Wrong element type");
if(((totalElements+1)*elementSize) > NODE_BODY_SIZE)//no space left
throw new NodeWriteException("Node.insertElement: Node size is becoming more than allowed");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
writeLastElement(elmt);
}
else{//else set the header values depending upon the new object header
writeLastElement(elmt);
}
//update the children's parents
if( elmt.getElementType() == Node.NONLEAF_NODE ){
try{
Node child = null;
if(fileHdr.isWriteThr()){
child = new Node(file, fileName, elmt.getPtr(), fileHdr);
RTree.chdNodes.remove(fileName, child.getNodeIndex());
}
else{
child = RTree.chdNodes.getNode(file, fileName, elmt.getPtr(), fileHdr);
}
child.setParent(nodeIndex);
}
catch(Exception e){
throw new NodeWriteException("Node.insertElement: " + e.getMessage());
}
}
}
/**
This func. writes the element to the last position.
It also takes care of updating the node header.
Also updates the local variables.
Also takes care whether it is the first element in the node or not.
If it is the first element then it sets the node header accordingly.
Least error checking - use it with care.
If the node is old and the new element type is of different type then it
will change the node header to the new type - so be very very careful!
In short don't call this method to insert an element different from the
already present element type.
*/
private void writeLastElement(Element elmt)
throws NodeWriteException
{
//taking backup in case of rollback
int oldElementSize = elementSize;
int oldElementType = elementType;
int oldTotalElements = totalElements;
//boolean oldIsNodeEmpty = isNodeEmpty;
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
try{
//setting local variables first
if(elmt instanceof LeafElement){
//size of the element
elementSize = LeafElement.sizeInBytes();
elementType = LEAF_NODE;
}
else{
//size of the element
elementSize = NonLeafElement.sizeInBytes();
elementType = NONLEAF_NODE;
}
if(fileHdr.isWriteThr()){
//byte[] data = new byte[elementSize];
ByteArrayOutputStream bs = new ByteArrayOutputStream(elementSize);
DataOutputStream ds = new DataOutputStream(bs);
ds.writeInt(elmt.getRect().getMinX());
ds.writeInt(elmt.getRect().getMinY());
ds.writeInt(elmt.getRect().getMaxX());
ds.writeInt(elmt.getRect().getMaxY());
ds.writeLong(elmt.getPtr());//see [2] - replace elements with elmt
bs.flush();
ds.flush();
//write to the file
seekLastElement();//uses var.
file.write(bs.toByteArray());
setDirty(false);
}else
setDirty(true);
//write the node header
writeNodeHeader(nodeIndex,totalElements+1,parent,elementSize,elementType);
//local variables
//isNodeEmpty = false;
elements[totalElements-1] = elmt;
nodeMBR.expandToInclude(elmt.getRect());//remove
}
catch(Exception e){
//e.printStackTrace();
//if anything goes wrong then set 'totalElements' will not
//be set hence it remains '0'
elementSize = oldElementSize;
elementType = oldElementType;
totalElements = oldTotalElements;
//isNodeEmpty = oldIsNodeEmpty;
throw new
NodeWriteException("Node.writeLastElement: Can't write element to file");
}
}
/**to add more than onr element at the end.
As elements are allocated to this node, each allocated element's children node's parent are reset.
This is simply because the new node index(for <code>elmts</code>) would be different from the old
ones(if any). none of the element should be null.
<br><b>Note:-</b> Giving <code>updateChldrn</code> <code>true</code> will not update the parent from
cache, but would actually update the parent on disk.
@param elmts The elements that are to be entered. None of them should be null.
@param updateChldrn Whether to update the children. When we are moving from root to leaves,
this should be false.
*/
public void insertElement(Element[] elmts, boolean updateChldrn)
throws NodeWriteException, NodeFullException
{
//check for space
if(totalElements == MAX)
throw new NodeFullException("Node.insertElement: Node full or not adequate space");
//check if it is an empty node or not
//if(!isNodeEmpty){//if not empty
if(totalElements > 0){//if not empty
if(elmts[0].getElementType() != elementType)
throw new NodeWriteException("Node.insertElement: Wrong element type");
if(((totalElements+elmts.length)*elementSize) > NODE_BODY_SIZE)//no space left
throw new NodeWriteException("Node.insertElement: Node size is becoming more than allowed");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
writeLastElements(elmts);
}
else{//else set the header values depending upon the new object header
writeLastElements(elmts);
}
if(!updateChldrn)
return;
//update the children's parent from the disk and not the cache.
if( elmts[0].getElementType() == Node.NONLEAF_NODE ){
try{
for(int i=0; i<elmts.length; i++){
if(elmts[i].getPtr() == Node.NOT_DEFINED)
continue;
Node child = null;
if(fileHdr.isWriteThr()){
child = new Node(file, fileName, elmts[i].getPtr(), fileHdr);
RTree.chdNodes.remove(fileName, child.getNodeIndex());
}else
child = RTree.chdNodes.getNode(file, fileName, elmts[i].getPtr(), fileHdr);
//child = new Node(file, fileName, elmts[i].getPtr(), fileHdr);
child.setParent(nodeIndex);
}
}catch(Exception e){
e.printStackTrace();
throw new NodeWriteException("Node.insertElement: " + e.getMessage());
}
}
}
/**
This method helps in bulk loading.
This func. writes the elements to the last position.
It also takes care of updating the node header.
Also updates the local variables.
Also takes care whether these are the first elements in the node or not.
If these <i>are</i> the first element then it sets the node header accordingly.
Least error checking - use it with care.
If the node is old and the new element type is of different type then it
will change the node header to the new type - so be very very careful!
In short don't call this method to insert an element different from the
already present element type.
*/
private void writeLastElements(Element[] elmts)
throws NodeWriteException
{
//taking backup in case of rollback
int oldElementSize = elementSize;
int oldElementType = elementType;
int oldTotalElements = totalElements;
//boolean oldIsNodeEmpty = isNodeEmpty;
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
try{
//setting local variables first
if(elmts[0] instanceof LeafElement){
//size of the element
elementSize = LeafElement.sizeInBytes();
elementType = LEAF_NODE;
}
else{
//size of the element
elementSize = NonLeafElement.sizeInBytes();
elementType = NONLEAF_NODE;
}
ByteArrayOutputStream bs = null;
DataOutputStream ds = null;
//write node header
if(fileHdr.isWriteThr()){
setDirty(false);
bs = new ByteArrayOutputStream(Node.NODE_SIZE);
ds = new DataOutputStream(bs);
writeNodeHeader(nodeIndex, totalElements+elmts.length, parent, elementSize, elementType, ds);
//write the existing elements
for(int i=0; i<oldTotalElements; i++){
ds.writeInt(elements[i].getRect().getMinX());
ds.writeInt(elements[i].getRect().getMinY());
ds.writeInt(elements[i].getRect().getMaxX());
ds.writeInt(elements[i].getRect().getMaxY());
ds.writeLong(elements[i].getPtr());
}
}else{
writeNodeHeader(nodeIndex, totalElements+elmts.length, parent, elementSize, elementType, ds);
setDirty(true);
}
//write the new elements
for(int i=0; i<elmts.length; i++){
nodeMBR.expandToInclude(elmts[i].getRect());//remove
elements[oldTotalElements+i] = elmts[i];
if(fileHdr.isWriteThr()){
ds.writeInt(elmts[i].getRect().getMinX());
ds.writeInt(elmts[i].getRect().getMinY());
ds.writeInt(elmts[i].getRect().getMaxX());
ds.writeInt(elmts[i].getRect().getMaxY());
ds.writeLong(elmts[i].getPtr());
}
}
if(fileHdr.isWriteThr()){
bs.flush();
ds.flush();
//write to the file
seekNode(nodeIndex);
file.write(bs.toByteArray());
}
//local variables
//isNodeEmpty = false;
}
catch(Exception e){
e.printStackTrace();
//if anything goes wrong then set 'totalElements' will not
//be set hence it remains '0'
elementSize = oldElementSize;
elementType = oldElementType;
totalElements = oldTotalElements;
//isNodeEmpty = oldIsNodeEmpty;
try{
writeNodeHeader(nodeIndex, totalElements+elmts.length, parent, elementSize, elementType);
}catch(Exception ex){throw new NodeWriteException(ex.getMessage());}
throw new NodeWriteException("Node.writeLastElement: Can't write element to file");
}
}
/**this function simply places the file pointer, it does not check for any
condition like placing the pointer beyond the end of file
*/
private void seekCurrNode() throws IOException
{
file.seek(FILE_HDR_SIZE + (nodeIndex * NODE_SIZE));
}
/**seek the specified node*/
private void seekNode(long nodeIdx) throws IOException
{
file.seek(FILE_HDR_SIZE + (nodeIdx * NODE_SIZE));
}
/**Seek the last element of the node. No checking*/
private void seekLastElement() throws IOException
{
file.seek(FILE_HDR_SIZE + (nodeIndex * NODE_SIZE)
+ (NODE_HDR_SIZE) +(elementSize * totalElements));
}
/**seek the specified element.No checking*/
private void seekElement(int elmtIndex) throws IOException
{
file.seek(FILE_HDR_SIZE + (nodeIndex * NODE_SIZE)
+ (NODE_HDR_SIZE) + (elementSize * elmtIndex));
}
/**seek the specified element's pointer.No checking*/
private void seekElementPtr(int elmtIndex) throws IOException
{
file.seek(FILE_HDR_SIZE + (nodeIndex * NODE_SIZE)
+ (NODE_HDR_SIZE) + (elementSize * elmtIndex)
+ Rect.sizeInBytes());
}
public int getElementType()
{
return elementType;
}
/**this func. writes the passed info to the current node's header.
Also updates the local variables.
*/
private void writeNodeHeader(long nodeIdx,int totElmt,long prnt,int elmtSz,int elmtTp)
throws IOException, NodeWriteException
{
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
if(fileHdr.isWriteThr()){
ByteArrayOutputStream bs = new ByteArrayOutputStream(FILE_HDR_SIZE);
DataOutputStream ds = new DataOutputStream(bs);
ds.writeInt(totElmt);//total elements
ds.writeLong(prnt);//parent
ds.writeInt(elmtSz);//element size
ds.writeInt(elmtTp);//element type
bs.flush();
ds.flush();
//write to the file
seekNode(nodeIdx);
file.write(bs.toByteArray());
setDirty(false);
}else
setDirty(true);
//update local variables
totalElements = totElmt;
parent = prnt;
elementSize = elmtSz;
elementType = elmtTp;
}
/**
This method will write the node header into the <code>ds</code>. Will also update the local variables.
It is assumed that the write pointer is correctly set in the o/p stream.
*/
private void writeNodeHeader(long nodeIdx, int totElmt,long prnt,int elmtSz,int elmtTp,
DataOutputStream ds)
throws IOException, NodeWriteException
{
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
if(fileHdr.isWriteThr()){
ds.writeInt(totElmt);//total elements
ds.writeLong(prnt);//parent
ds.writeInt(elmtSz);//element size
ds.writeInt(elmtTp);//element type
ds.flush();
setDirty(true);
}else
setDirty(true);
//update local variables
totalElements = totElmt;
parent = prnt;
elementSize = elmtSz;
elementType = elmtTp;
}
/**
will return the index of the node.
*/
public long getNodeIndex()//for new nodes
{
return(nodeIndex);
}
private void refreshNode()//see wherever it is called from for writethr
throws IOException
{
try{
byte[] data = new byte[NODE_SIZE];
seekCurrNode();
//read the whole node
file.read(data);
//get the header details into the variables
DataInputStream ds = new DataInputStream(new ByteArrayInputStream(data));
totalElements = ds.readInt();
parent = ds.readLong();//ds.readInt();
elementSize = ds.readInt();
elementType = ds.readInt();
if(totalElements <= 0)//set local variable
return;
// else
// isNodeEmpty = false;
//get the elements if present
if(totalElements < 1)
return;
nodeMBR = new Rect();//remove
int minX,minY,maxX,maxY;
for(int i=0; i< totalElements; i++){
//read the points
minX = ds.readInt();
minY = ds.readInt();
maxX = ds.readInt();
maxY = ds.readInt();
Rect rectangle = new Rect(minX,minY,maxX,maxY);
nodeMBR.expandToInclude(rectangle);//remove
if(elementType == LEAF_NODE){// modified see [1]
long ptr = ds.readLong();
elements[i] = new LeafElement(rectangle,ptr);
}else if(elementType == NONLEAF_NODE){//if non leaf type then...
long nodePtr = ds.readLong();
elements[i] = new NonLeafElement(rectangle,nodePtr);
}
}
ds.close();
}
catch(Exception e){
throw new IOException("Node.refreshNode : Can't read from node header " + e.getMessage());
}
}
Rect[] getAllRectangles()
throws IllegalValueException
{
if(totalElements == 0)
throw new IllegalValueException("Node.getAllRectangles: No elements in the node");
Rect[] rects = new Rect[totalElements];
for(int i=0; i<totalElements; i++){
rects[i] = elements[i].getRect();
}
return rects;
}
/**
Returns the element(of the current node) whose rectangle needs the least
enlargment to include <code>elmt</code>.
The logic assumes that the elements are not sorted.
See the documentation for least enlargement logic.
*/
public Element getLeastEnlargement(Element elmt)
throws NodeEmptyException, IllegalValueException, NodeWriteException
{
if(elmt == null)
throw new IllegalValueException("Node.getBestFitElement : Element is null");
//if(isNodeEmpty){//if there are no elements in the node
if(totalElements <= 0){//if there are no elements in the node
throw new NodeEmptyException("Node.getBestFitElement : Node does not have any elements");
}
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
Element retElmt;//initialize with first element
int area;
//get area of first MBR and call it the least area
int leastArea=(elements[0].getRect().getResultingMBR(elmt.getRect())).getArea();
leastArea -= elements[0].getRect().getArea();
if(elementType == Node.LEAF_NODE)
//Integer intVal = (Integer)elements[0].getPtr();//the org. code
retElmt = new LeafElement(elements[0].getRect(), elements[0].getPtr() );
else
retElmt = new NonLeafElement(elements[0].getRect(), elements[0].getPtr());
//now check each of the elements
for(int i=1; i < totalElements; ++i){
//get area of the MBR of the two rects.
area=(elements[i].getRect().getResultingMBR(elmt.getRect())).getArea();
//remove the area of the encapsulating rect.
area -= elements[i].getRect().getArea();
//check if it is the smallest rect
if(leastArea > area){
leastArea = area;
if(elementType == Node.LEAF_NODE)
retElmt = new LeafElement(elements[i].getRect(), elements[i].getPtr());
else
retElmt = new NonLeafElement(elements[i].getRect(), elements[i].getPtr());
}
else if(leastArea == area){//Resovle ties by choosing the entry with the rectangle of smallest area."
if(retElmt.getRect().getArea() >= elements[i].getRect().getArea()){
leastArea = area;
if(elementType == Node.LEAF_NODE)
retElmt = new LeafElement(elements[i].getRect(), elements[i].getPtr());
else
retElmt = new NonLeafElement(elements[i].getRect(),elements[i].getPtr());
}
}
}
return(retElmt);
}
/**
@return a boolean describing whether there is space for another element
*/
boolean isInsertPossible()
{
if(totalElements >= MAX)
return false;
else
return true;
}
/**
See ./Logic.txt
Linear split Algo.
February 1003 - Now quad split algo.
@return First node would be the original node.Second would be the new one.
@param Element The new element to be inserted
@param slotIndex The index of the slot of this tree if any, else give NOT_DEFINED.
*/
public Node[] splitNode(Element elmtM1, long slotIndex)
throws RTreeException, NodeWriteException
{
if((totalElements < MAX) || (elmtM1.getElementType() != elementType))
throw new RTreeException("Node.splitNode: Node is not full or new element is of wrong type");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
try{
int rem = totalElements+1;//no. of elements remaining + the new element
Element[] elmtPlusOne = new Element[rem];
for(int i=0;i<rem-1;i++)
elmtPlusOne[i] = elements[i];
elmtPlusOne[totalElements] = elmtM1;
//the elements which have been allocated: 1 - present, 0 - absent
int[] elmtsGone = new int[rem];
for(int i=0; i<elmtsGone.length; ++i)
elmtsGone[i] = 1;//all elements present
int elmtsInA = 0;//total elements in A
int elmtsInB = 0;//total elements in B
Rect mbrA;
Rect mbrB;
//int[] seeds = lnrPickSeeds(elmtPlusOne);//the two seed
int[] seeds = quadPickSeeds(elmtPlusOne);//the two seed
int elmtType = elmtPlusOne[0].getElementType();
//never do any file read or write on the old node now as the new nodes
//could very well be the old node from the free list.
//Although they would be freed explicitly later.
Node nodeA, nodeB;
if(fileHdr.isWriteThr()){
nodeA = new Node(file,fileName,parent,elmtType,fileHdr);
nodeB = new Node(file,fileName,parent,elmtType,fileHdr);
}else{
nodeA = RTree.chdNodes.getNode(file,fileName,parent,elmtType,fileHdr);
nodeB = RTree.chdNodes.getNode(file,fileName,parent,elmtType,fileHdr);
}
nodeA.insertElement(elmtPlusOne[seeds[0]]);
nodeB.insertElement(elmtPlusOne[seeds[1]]);
//update the MBRs
mbrA = elmtPlusOne[seeds[0]].getRect();
mbrB = elmtPlusOne[seeds[1]].getRect();
//update the variables
elmtsInA++;//Inc. 'A' count by 1
elmtsGone[seeds[0]] = 0;//mark the element assigned
elmtsInB++;//Inc. 'B' count by 1
elmtsGone[seeds[1]] = 0;//mark the element assigned
rem -= 2;//less the no. of elements still left
//a loop till the elements last
while(rem > 0){
//if A needs all to equate m then give it all the elements - see Guttman
if((Node.MIN - elmtsInA) == rem){
for(int i=0; i < elmtsGone.length; i++){
//check if the elmt. is already assigned
if(elmtsGone[i] == 1){
nodeA.insertElement(elmtPlusOne[i]);//read variable
elmtsGone[i] = 0;//element now is absent
elmtsInA++;
rem--;
//find the new MBR fro A
/*Commented because not required
mbrA = Rect.getResultingMBR(mbrA,elmtPlusOne[i].getRect());
*/
//the end of the loop and method
}
}
}
//if B needs all to equate m then give it all the elements-see Guttman
else if((Node.MIN - elmtsInB) == rem){
for(int i=0; i < elmtsGone.length; i++){
//check if the elmt. is already assigned
if(elmtsGone[i] == 1){
nodeB.insertElement(elmtPlusOne[i]);//read variable
elmtsGone[i] = 0;//element now is absent
elmtsInB++;
rem--;
//find the new MBR fro B
/*Commented because not required
mbrB = Rect.getResultingMBR(mbrB,elmtPlusOne[i].getRect());
*/
//the end of the loop and method
}
}
}
//if both are ok
else{
int i=-1;
//loop till an unassigned element is found
try{
while((++i<elmtsGone.length)&&(elmtsGone[i] == 0));
}catch(Exception e){
System.out.println("Node.splitNode: trouble in paradise");
//System.exit(1);
}
/*Above statement can be troublesome, in that case use the
below logic
for(int i=0; i < elmtsGone.length; i++)
if(elmtsGone[i] == 0)
break;
*/
//find MBR for both the groups
Rect newMBRA = elmtPlusOne[i].getRect().getResultingMBR(mbrA);
Rect newMBRB = elmtPlusOne[i].getRect().getResultingMBR(mbrB);
//remove the original area
int newAreaA = newMBRA.getArea() - mbrA.getArea();
int newAreaB = newMBRB.getArea() - mbrB.getArea();
//find which group to enter - see - 'Guttman(QS3)
//A needs least enlargement
if (newAreaA < newAreaB){
nodeA.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInA++;
rem--;
mbrA = newMBRA;
}
//B needs least enlargement
else if(newAreaA > newAreaB){
nodeB.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInB++;
rem--;
mbrB = newMBRB;
}
//if equal but A is smaller
else if(mbrA.getArea() < mbrB.getArea()){
nodeA.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInA++;
rem--;
mbrA = newMBRA;
}
//if equal but B is smaller
else if(mbrA.getArea() > mbrB.getArea()){
nodeB.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInB++;
rem--;
mbrB = newMBRB;
}
//also equal in area elmts. in A are less
else if(elmtsInA < elmtsInB){
nodeA.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInA++;
rem--;
mbrA = newMBRA;
}
//also equal in area elemts. in B are less
else if(elmtsInA > elmtsInB){
nodeB.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInB++;
rem--;
mbrB = newMBRB;
}
//choose any one
else{
nodeA.insertElement(elmtPlusOne[i]);//read local variable
elmtsGone[i] = 0;//element now is absent
elmtsInA++;
rem--;
mbrA = newMBRA;
}
}
}
/*
Adjust the parent element so that it points to the first nodeA.
*/
if(parent != slotIndex){//NOT_DEFINED){
Node parentN = null;
if(fileHdr.isWriteThr())
parentN = new Node(file,fileName,parent,fileHdr);
else
parentN = RTree.chdNodes.getNode(file,fileName,parent,fileHdr);
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,parent);
//get the parent element of nodes[0]
int parentElmtIndex = parentN.getElementIndex(nodeIndex);
parentN.modifyElement(parentElmtIndex, nodeA.getNodeIndex());
}
Node[] ret = new Node[2];
ret[0] = nodeA;
ret[1] = nodeB;
deleteNode();
return(ret);
}catch(Exception e){
e.printStackTrace();
throw new RTreeException("Node.nodeSplit : " + e.getMessage());
}
}
/**
* The linear Pick Seed method from Guttman.
* Assuming that we have only 2 dimensions.
* <br>this method is no longer used (certainly not deprecated), instead <code>quadPickSeeds</code>
* is being used
* @return the two picked indexes from the node
*/
private int[] lnrPickSeeds(Element[] elmts)
throws IllegalValueException
{
if(elmts.length <= 1)
throw new IllegalValueException("Node.lnrPickSeed : PickSeed not possible as there are no elements");
//find the MBR of the set.
Rect mbr = elmts[0].getRect();
for(int i=1; i<elmts.length; ++i)
mbr = elmts[i].getRect().getResultingMBR(mbr);
//"Find Extreme Rectangles along all dimensions" - Guttman
//find the highest low side and lowest high side.
int hlsX = Integer.MIN_VALUE;//highest low side for X
int hlsY = Integer.MIN_VALUE;// ..
int lhsX = Integer.MAX_VALUE;// ..
int lhsY = Integer.MAX_VALUE;// ..
int hlsIdxX = Integer.MAX_VALUE;//highest low side index for X
int hlsIdxY = Integer.MAX_VALUE;// ..
int lhsIdxX = Integer.MAX_VALUE;// ..
int lhsIdxY = Integer.MAX_VALUE;// ..
for(int i=0; i<elmts.length; ++i){
//highest low side for X
if(elmts[i].getRect().getMinX() >= hlsX){
hlsX = elmts[i].getRect().getMinX();
hlsIdxX = i;
}
//highest low side for Y
if(elmts[i].getRect().getMinY() >= hlsY){
hlsY = elmts[i].getRect().getMinY();
hlsIdxY = i;
}
//lowest high side for X
if(elmts[i].getRect().getMaxX() <= lhsX){
lhsX = elmts[i].getRect().getMaxX();
lhsIdxX = i;
}
//lowest high side for Y
if(elmts[i].getRect().getMaxY() <= lhsY){
lhsY = elmts[i].getRect().getMaxY();
lhsIdxY = i;
}
}
//"Normalize the separations" - Guttman
//divide the separations along each dimensions by the width of the MBR
int[] retIdx = new int[2];
int Xpair = (Math.abs(lhsX - hlsX))/mbr.getWidth();
int Ypair = (Math.abs(lhsY - hlsY))/mbr.getHeight();
if(Xpair > Ypair){
//if both are the same rect then choose randomly
if(hlsIdxX == lhsIdxX){
if(hlsIdxX != 0)
hlsIdxX = 0;
else
hlsIdxX = 1;
}
retIdx[0] = hlsIdxX;
retIdx[1] = lhsIdxX;
}
else if(Xpair < Ypair){
/*if both are the same rect then choose randomly - an accidental
discovery*/
if(hlsIdxY == lhsIdxY){
if(hlsIdxY != 0)
hlsIdxY = 0;
else
hlsIdxY = 1;
}
retIdx[0] = hlsIdxY;
retIdx[1] = lhsIdxY;
}
else{//if normalized values are equal
//choose the pair with the least separation(not normalized sap.)
if((Math.abs(lhsX - hlsX)) >= (Math.abs(lhsY - hlsY))){
if(hlsIdxX == lhsIdxX){
if(hlsIdxX != 0)
hlsIdxX = 0;
else
hlsIdxX = 1;
}
retIdx[0] = hlsIdxX;
retIdx[1] = lhsIdxX;
}
else{
if(hlsIdxY == lhsIdxY){
if(hlsIdxY != 0)
hlsIdxY = 0;
else
hlsIdxY = 1;
}
retIdx[0] = hlsIdxY;
retIdx[1] = lhsIdxY;
}
}
return(retIdx);
}
/**
The quadratic Pick Seed method from Guttman.
Assuming that we have only 2 dimensions. This method is slightly slower than the linear method but
it results in better splits, besides it is much easier to implement.
@return the two picked indexes from the node
*/
private int[] quadPickSeeds(Element[] elmts)
throws IllegalValueException
{
if(elmts.length <= 1)
throw new IllegalValueException("Node.quadPickSeed : PickSeed not possible as there are no elements");
int retIdx[] = new int[2];
int mostIneff = Integer.MIN_VALUE;//area of the most inefficient pair
for(int i=0;i<elmts.length;i++){
for(int j=i+1;j<elmts.length;j++){
Rect seedRect = Rect.getResultingMBR(elmts[i].getRect(), elmts[j].getRect());
int arr = seedRect.getArea() - elmts[i].getRect().getArea() - elmts[j].getRect().getArea();
//if this is the most inefficient pair
if(arr > mostIneff){
retIdx[0] = i;
retIdx[1] = j;
mostIneff = arr;
}
}
}
return retIdx;
}
public long getParent()
{
return(parent);
}
/**
* returns index of the element with the pointer passed in the parameter
* @param Object Depends upon the Element type.
* @return returns NOT_DEFINED if ptr is not found
*/
public int getElementIndex(long ptr/*Object ptr*/)
{
if(totalElements < 1)
return NOT_DEFINED;
for(int i=0; i<totalElements; i++){
if(elements[i].getPtr() == ptr)
return i;
}
System.out.println("Node.getElementIndex: Element not found, returning NOT_DEFINED");
//Even if object types do not match it will return NOT_DEFINED
return NOT_DEFINED;//if nothing found
}
/**
Used to overwrite the old Element with the new one.
It modifies the element in the disk as well as in the local variables.
*/
public void modifyElement(int index,Element elmt)
throws IllegalValueException,IOException, NodeWriteException
{
if((index > totalElements) || (index < 0) || (elmt == null))
throw new IllegalValueException("Node.modifyElmtMBR : index out of bound or MBR is null");
if(elmt.getElementType() != elementType)
throw new IllegalValueException("Node.modifyElmtMBR : Element of wrong type");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
if(fileHdr.isWriteThr()){
ByteArrayOutputStream bs = new ByteArrayOutputStream(elementSize);
DataOutputStream ds = new DataOutputStream(bs);
ds.writeInt(elmt.getRect().getMinX());
ds.writeInt(elmt.getRect().getMinY());
ds.writeInt(elmt.getRect().getMaxX());
ds.writeInt(elmt.getRect().getMaxY());
ds.writeLong(elmt.getPtr());//see [2]
bs.flush();
ds.flush();
//write to the file
seekElement(index);
file.write(bs.toByteArray());
setDirty(false);
}else
setDirty(true);
//adjsut in the local variable
elements[index].setRect(elmt.getRect());
elements[index].setPtr(elmt.getPtr());
//we do not recalculate the whole mbr if we do not need to
if(elmt.getRect().contains(elements[index].getRect()))//if previous MBR was smaller...
nodeMBR.expandToInclude(elmt.getRect());
else//i guess we need to calculate
refreshNodeMBR();//remove
}
/**
Overloaded
*/
public void modifyElement(int index,long pointer)
throws IllegalValueException,IOException, NodeWriteException
{
if((index > totalElements) || (index < 0)){
try{
throw new IllegalValueException("Node.modifyElmtMBR : index out of bound for node "+nodeIndex);
}catch(Exception e){
e.printStackTrace();
}
}
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
if(fileHdr.isWriteThr()){
ByteArrayOutputStream bs = new ByteArrayOutputStream(LONG_SIZE);
DataOutputStream ds = new DataOutputStream(bs);
ds.writeLong(pointer);//ds.writeInt(pointer);
bs.flush();
ds.flush();
//write to the file
seekElementPtr(index);
file.write(bs.toByteArray());
setDirty(false);
}
else
setDirty(true);
//adjsut in the local variable
elements[index].setPtr(pointer);
}
/**
Overloaded
*/
public void modifyElement(int index,Rect rect)
throws IllegalValueException,IOException, NodeWriteException
{
if((index > totalElements) || (index < 0))
throw new IllegalValueException("Node.modifyElmtMBR : index out of bound or MBR is null");
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
if(fileHdr.isWriteThr()){
ByteArrayOutputStream bs = new ByteArrayOutputStream(Rect.sizeInBytes());
DataOutputStream ds = new DataOutputStream(bs);
ds.writeInt(rect.getMinX());
ds.writeInt(rect.getMinY());
ds.writeInt(rect.getMaxX());
ds.writeInt(rect.getMaxY());
bs.flush();
ds.flush();
//write to the file
seekElement(index);
file.write(bs.toByteArray());
setDirty(false);
}else
setDirty(true);
//adjsut in the local variable
elements[index].setRect(rect);
//remove
//we do not recalculate the whole mbr if we do not need to
if(rect.contains(elements[index].getRect()))
nodeMBR.expandToInclude(rect);
else//i guess we need to calculate
refreshNodeMBR();//remove
}
/**
This function runs a loop on the elements to calculate the total MBR.
Therefore in case if you already have loop that runs through each of the entries,
then it is better to calculate MBR in that loop without calling this method.
@throws IllegalValueException When there are no elements in the node.
*/
public Rect getNodeMBR()
throws IllegalValueException
{
//remove
if(totalElements < 1)
throw new IllegalValueException("Node.getNodeMBR: Node empty");
return nodeMBR;
//In case of trouble remove the above line and commission the below written lines
/*
Rect ret = elements[0].getRect();
for(int i=1; i<totalElements; i++)
ret = Rect.getResultingMBR(ret, elements[i].getRect());
return ret;
*/
}
/**
Will refresh the <code>nodeMBR</code> for the <code>elements</code>
*/
private void refreshNodeMBR()
{
nodeMBR = new Rect();
for(int i=0; i<totalElements; i++)
nodeMBR.expandToInclude(elements[i].getRect());
}
/**
No error echecking at all.
*/
public void setParent(long /*int*/ prnt)
throws IOException, NodeWriteException
{
if(prnt == NOT_DEFINED)//if this is the new root then update the file hdr
fileHdr.writeFileHeader(fileHdr.totalNodes,nodeIndex);
if(fileHdr.isWriteThr())
RTree.chdNodes.remove(fileName,nodeIndex);
writeNodeHeader(nodeIndex,totalElements,prnt,elementSize,elementType);
}
/**
Obvious, isn't it?
*/
public String toString()
{
String ret = "\n\t***Node at Index: "+Long.toString(nodeIndex)+"***";
ret += "\nLocal Variables-";
//ret += "\n\tFile: " + fileName;
ret += "\n\tnodeIndex: "+ Long.toString(nodeIndex);
if (totalElements <= 0){
//ret += "\n\tisNodeEmpty: true";
ret += "\n\tnodeMBR: isNull";
}
// else{
// ret += "\n\tisNodeEmpty: false";
// //ret += "\n\tnodeMBR: " + nodeMBR.toString();
// }
ret += "\nFile Header-";
ret += "\n\ttotalNodes: " + Integer.toString(fileHdr.totalNodes);
ret += "\n\trootIndex: " + Long.toString(fileHdr.rootIndex);
ret += "\nNode Header-";
ret += "\n\ttotalElements: " + Integer.toString(totalElements);
ret += "\n\tparent: " + Long.toString(parent);
ret += "\n\telementSize:" + Integer.toString(elementSize);
ret += "\n\telementType:" + Integer.toString(elementType);
for(int i=0;i<totalElements;i++)
ret += elements[i].toString();
return ret;
}
public int getTotalElements()
{
return totalElements;
}
/**
Although it returns all the elements but the total elements will not be equal to the length of
the returned array.Therefore <br><b>Never Use <code>.length</code> field With the Returned Array
</b>. Instead use <code>getTotalElements()</code>.
@return An element Array.
*/
public Element[] getAllElements()
{
return elements;
}
Element getElement(int index)
throws IllegalValueException
{
if((index < 0) || (index > totalElements-1))
throw new IllegalValueException("Node.getElement Index out of bound");
return elements[index];
}
/**
Adds the node to the free stack.
Be very careful with this method because once called, this node may be
given to any new node even when you have not destroyed its object.
If the node is the only node then it updates the file header as well.
</br><i><b>Once called, there is no turning back!</b></i>.
*/
public void deleteNode()
throws NodeWriteException
{
setDirty(false);//this is intentional
RTree.chdNodes.remove(fileName,nodeIndex);//we do not check for writeThr here
try{
fileHdr.push(nodeIndex);
}catch(StackOverflowException e){
}catch(IOException ex){
throw new NodeWriteException(ex.getMessage());
}
}
/**
* This method is added to sort the elements in this node to help sweepline algorithm.
*/
void sweepSort()//check out for null elements
{
if(elements != null && elements.length > 1 && sorted == false){
Arrays.sort(elements, 0, totalElements, new rtree.join.CompElmtX());
sorted = true;
}//if
}//sweepSort
/**
This is a new methos that will help the phylosophy where one should write to tbe cache only when
required.
@return true if needed write and written or false (not dirty).
*/
public boolean flush()
throws NodeWriteException
{
try{
if(dirty && !fileHdr.isWriteThr()){
deleteElement(-1, true);
setDirty(false);
return true;
}else
return false;
}catch(Exception e){
e.printStackTrace();
throw new NodeWriteException(e.getMessage());
}
}
void setDirty(boolean val)
{
if(val)//dirty
sorted = false;
dirty = val;
}
public boolean isDirty()
{
return dirty;
}
}