// //RTree implementation. //Copyright (C) 2002-2004 Wolfgang Baer - WBaer@gmx.de // //This library is free software; you can redistribute it and/or //modify it under the terms of the GNU Lesser General Public //License as published by the Free Software Foundation; either //version 2.1 of the License, or (at your option) any later version. // //This library is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //Lesser General Public License for more details. // //You should have received a copy of the GNU Lesser General Public //License along with this library; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package org.deegree.io.rtree; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Stack; /** * <p> * A persistent implementation of a PageFile implemented based on * a RandomAccesFile. * </p> * <p> * Structure of the File<br> * <br><br> * -- Header --<br> * int pageFileVersion<br> * int dimension<br> * int capacity = maxLoad + 1 for Overflow<br> * int minimum<br> * <br><br> * -- Body --<br> * a sequence of page one after another with:<br> * int typ - 1 LeafNode 2 NoneLeafNode<br> * int place - index of entry of this node in father node<br> * int counter - current used space in the node<br> * int parentNode - page number of father node<br> * int pageNumber - own page number<br> * - for(i = 0; i < capacity; i++)<br> * int data Entry i - page number of childnode or object ID of data entry<br> * - always dependend on dimension = x<br> * double pMin x.Dimension - pMin of the common HyperBoundingBox<br> * double pMax x.Dimension - pMax of the common HyperBoundingBox<br> * - for(i = 0; i < capacity; i++)<br> * double pMin x.Dimension - pMin HyperBoundingBox for Entry i<br> * double pMax x.Dimension - pMax HyperBoundingBox for Entry i<br> * <br><br> * int entspr. 4 Bytes - double entspr. 8 Bytes<br> * <br><br> * PageSize = (4 * (5 + capacity)) + (capacity + 1) * (dimension * 16)<br> * <br> * </p> * @author Wolfgang Baer - WBaer@gmx.de */ class PersistentPageFile extends PageFile { /** magic number */ private static final int PAGEFILE_VERSION = 060676002; private static final int EMPTY_PAGE = -22; private RandomAccessFile file; private int pageSize; private String fileName; private byte[] buffer; private Stack emptyPages; private boolean closed; /** * Constructor * @param fileName */ protected PersistentPageFile(String fileName) { super(); this.fileName = fileName; this.emptyPages = new Stack(); this.closed = false; } /** * Initializes the PersistentPageFile. * Overrides initialize in PageFile. * @param dimension - dimension of the data * @param capacity - capacity of a node * @throws PageFileException */ protected void initialize(int dimension, int capacity) throws PageFileException { super.initialize( dimension, capacity ); File fileTest = new File( fileName ); try { if ( dimension == -999 ) { // Initialize from existing file if(!fileTest.exists()) throw new PageFileException("File does not exist"); file = new RandomAccessFile(fileTest, "rw"); // Test if it is a PersistentPageFile file.seek( 0 ); if(file.readInt() != PAGEFILE_VERSION) throw new PageFileException("Not a PersistentPageFile or wrong version"); // Reading header - Initializing PageFile this.dimension = file.readInt(); this.capacity = file.readInt(); this.minimum = file.readInt(); this.pageSize = ((4 * (5 + this.capacity)) + ((this.capacity + 1) * (this.dimension * 16))); this.buffer = new byte[pageSize]; // reading empty pages in Stack int i = 0; try { while ( true ) { file.seek( 16 + ( i * pageSize ) ); if(EMPTY_PAGE == file.readInt()) emptyPages.push( new Integer( i ) ); i++; } } catch(EOFException eof) { // not an exception - wanted } } else { // new file file = new RandomAccessFile( fileTest, "rw" ); file.setLength( 0 ); this.pageSize = ((4 * (5 + capacity)) + ((capacity + 1) * (dimension * 16))); this.buffer = new byte[pageSize]; // writing header file.seek( 0 ); file.writeInt( PAGEFILE_VERSION ); file.writeInt( this.dimension ); file.writeInt( this.capacity ); file.writeInt( this.minimum ); } } catch ( IOException e ) { e.fillInStackTrace(); throw new PageFileException( "IOException occured: \n " + e.getMessage() ); } } /** * @see PageFile#readNode(int) */ protected Node readNode(int pageFileNumber) throws PageFileException { Node node = null; try { file.seek(16 + (pageFileNumber * pageSize)); int read = file.read( buffer ); if ( pageSize == read ) { DataInputStream ds = new DataInputStream( new ByteArrayInputStream( buffer ) ); int type = ds.readInt(); if(type == 1) node = new LeafNode( -1, this ); else node = new NoneLeafNode( -1, this ); node.place = ds.readInt(); node.counter = ds.readInt(); node.parentNode = ds.readInt(); node.pageNumber = ds.readInt(); if ( type == 1 ) { for ( int i = 0; i < capacity; i++ ) ( (LeafNode)node ).data[i] = ds.readInt(); } else { for ( int i = 0; i < capacity; i++ ) ( (NoneLeafNode)node ).childNodes[i] = ds.readInt(); } node.unionMinBB = readNextHyperBoundingBox( ds ); for ( int i = 0; i < capacity; i++ ) node.hyperBBs[i] = readNextHyperBoundingBox( ds ); ds.close(); } else { throw new PageFileException("Exception during read operation"); } return node; } catch ( IOException e ) { e.fillInStackTrace(); throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() ); } } // reads the next HyperBoundingBox from the byte buffer private HyperBoundingBox readNextHyperBoundingBox(DataInputStream ds) throws IOException { double[] point1, point2; point1 = new double[dimension]; point2 = new double[dimension]; for ( int i = 0; i < dimension; i++ ) point1[i] = ds.readDouble(); for ( int i = 0; i < dimension; i++ ) point2[i] = ds.readDouble(); return new HyperBoundingBox( new HyperPoint( point1 ), new HyperPoint( point2 ) ); } /** * @see PageFile#writeNode(Node) */ protected int writeNode(Node node) throws PageFileException { try { if ( node.pageNumber < 0 ) { if(!emptyPages.empty()) node.setPageNumber( ( (Integer)emptyPages.pop() ).intValue() ); else node.setPageNumber( (int)( ( file.length() - 16 ) / pageSize ) ); } ByteArrayOutputStream bs = new ByteArrayOutputStream( pageSize ); DataOutputStream ds = new DataOutputStream( bs ); int type; if(node instanceof LeafNode) type = 1; else type = 2; ds.writeInt( type ); ds.writeInt( node.place ); ds.writeInt( node.counter ); ds.writeInt( node.parentNode ); ds.writeInt( node.pageNumber ); if ( node instanceof LeafNode ) { for ( int i = 0; i < node.counter; i++ ) { ds.writeInt( ( (LeafNode)node ).data[i] ); } for ( int i = 0; i < ( capacity - node.counter ); i++ ) ds.writeInt( -1 ); } else { for ( int i = 0; i < node.counter; i++ ) { ds.writeInt( ( (NoneLeafNode)node ).childNodes[i] ); } for ( int i = 0; i < ( capacity - node.counter ); i++ ) ds.writeInt( -1 ); } for ( int i = 0; i < dimension; i++ ) ds.writeDouble( node.unionMinBB.getPMin().getCoord( i ) ); for ( int i = 0; i < dimension; i++ ) ds.writeDouble( node.unionMinBB.getPMax().getCoord( i ) ); for ( int j = 0; j < node.counter; j++ ) { for ( int i = 0; i < dimension; i++ ) ds.writeDouble( node.hyperBBs[j].getPMin().getCoord( i ) ); for ( int i = 0; i < dimension; i++ ) ds.writeDouble( node.hyperBBs[j].getPMax().getCoord( i ) ); } for ( int j = 0; j < ( capacity - node.counter ); j++ ) { for ( int i = 0; i < ( dimension * 2 ); i++ ) ds.writeDouble( -1 ); } ds.flush(); bs.flush(); file.seek( 16 + ( pageSize * node.pageNumber ) ); file.write( bs.toByteArray() ); ds.close(); return node.pageNumber; } catch ( IOException e ) { e.fillInStackTrace(); throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() ); } } /** * @see PageFile#deleteNode(int) */ protected Node deleteNode(int pageNumber) throws PageFileException { Node node = this.readNode( pageNumber ); try { file.seek( 16 + ( pageSize * node.pageNumber ) ); file.writeInt( EMPTY_PAGE ); } catch ( IOException e ) { e.fillInStackTrace(); throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() ); } emptyPages.push( new Integer( pageNumber ) ); return node; } /** * @see PageFile#close() */ protected void close() throws PageFileException { try { file.close(); } catch (IOException e) { e.fillInStackTrace(); throw new PageFileException( "PageFileException during close()" ); } closed = true; } protected void finalize() throws Throwable { if(!closed) file.close(); super.finalize(); } }/* ******************************************************************** Changes to this class. What the people have been up to: $Log: PersistentPageFile.java,v $ Revision 1.3 2006/07/12 14:46:17 poth comment footer added ********************************************************************** */