001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/rtree/PersistentPageFile.java $
002 //
003 //RTree implementation.
004 //Copyright (C) 2002-2004 Wolfgang Baer - WBaer@gmx.de
005 //
006 //This library is free software; you can redistribute it and/or
007 //modify it under the terms of the GNU Lesser General Public
008 //License as published by the Free Software Foundation; either
009 //version 2.1 of the License, or (at your option) any later version.
010 //
011 //This library is distributed in the hope that it will be useful,
012 //but WITHOUT ANY WARRANTY; without even the implied warranty of
013 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 //Lesser General Public License for more details.
015 //
016 //You should have received a copy of the GNU Lesser General Public
017 //License along with this library; if not, write to the Free Software
018 //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019
020 package org.deegree.io.rtree;
021
022 import java.io.ByteArrayInputStream;
023 import java.io.ByteArrayOutputStream;
024 import java.io.DataInputStream;
025 import java.io.DataOutputStream;
026 import java.io.EOFException;
027 import java.io.File;
028 import java.io.IOException;
029 import java.io.RandomAccessFile;
030 import java.util.Stack;
031
032 /**
033 * <p>
034 * A persistent implementation of a PageFile implemented based on a RandomAccesFile.
035 * </p>
036 * <p>
037 * Structure of the File<br>
038 * <br>
039 * <br> -- Header --<br>
040 * int pageFileVersion<br>
041 * int dimension<br>
042 * int capacity = maxLoad + 1 for Overflow<br>
043 * int minimum<br>
044 * <br>
045 * <br> -- Body --<br>
046 * a sequence of page one after another with:<br>
047 * int typ - 1 LeafNode 2 NoneLeafNode<br>
048 * int place - index of entry of this node in father node<br>
049 * int counter - current used space in the node<br>
050 * int parentNode - page number of father node<br>
051 * int pageNumber - own page number<br> - for(i = 0; i < capacity; i++)<br>
052 * int data Entry i - page number of childnode or object ID of data entry<br> - always dependend on
053 * dimension = x<br>
054 * double pMin x.Dimension - pMin of the common HyperBoundingBox<br>
055 * double pMax x.Dimension - pMax of the common HyperBoundingBox<br> - for(i = 0; i < capacity;
056 * i++)<br>
057 * double pMin x.Dimension - pMin HyperBoundingBox for Entry i<br>
058 * double pMax x.Dimension - pMax HyperBoundingBox for Entry i<br>
059 * <br>
060 * <br>
061 * int entspr. 4 Bytes - double entspr. 8 Bytes<br>
062 * <br>
063 * <br>
064 * PageSize = (4 * (5 + capacity)) + (capacity + 1) * (dimension * 16)<br>
065 * <br>
066 * </p>
067 *
068 * @author Wolfgang Baer - WBaer@gmx.de
069 */
070 class PersistentPageFile extends PageFile {
071
072 /** magic number */
073 private static final int PAGEFILE_VERSION = 060676002;
074
075 private static final int EMPTY_PAGE = -22;
076
077 private RandomAccessFile file;
078
079 private int pageSize;
080
081 private String fileName;
082
083 private byte[] buffer;
084
085 private Stack<Integer> emptyPages;
086
087 private boolean closed;
088
089 /**
090 * Constructor
091 *
092 * @param fileName
093 */
094 protected PersistentPageFile( String fileName ) {
095 super();
096 this.fileName = fileName;
097 this.emptyPages = new Stack<Integer>();
098 this.closed = false;
099 }
100
101 /**
102 * Initializes the PersistentPageFile. Overrides initialize in PageFile.
103 *
104 * @param dimension -
105 * dimension of the data
106 * @param capacity -
107 * capacity of a node
108 * @throws PageFileException
109 */
110 protected void initialize( int dimension, int capacity )
111 throws PageFileException {
112 super.initialize( dimension, capacity );
113
114 File fileTest = new File( fileName );
115
116 try {
117 if ( dimension == -999 ) {
118 // Initialize from existing file
119
120 if ( !fileTest.exists() )
121 throw new PageFileException( "File does not exist" );
122
123 file = new RandomAccessFile( fileTest, "rw" );
124 // Test if it is a PersistentPageFile
125 file.seek( 0 );
126 if ( file.readInt() != PAGEFILE_VERSION )
127 throw new PageFileException( "Not a PersistentPageFile or wrong version" );
128
129 // Reading header - Initializing PageFile
130 this.dimension = file.readInt();
131 this.capacity = file.readInt();
132 this.minimum = file.readInt();
133 this.pageSize = ( ( 4 * ( 5 + this.capacity ) ) + ( ( this.capacity + 1 ) * ( this.dimension * 16 ) ) );
134 this.buffer = new byte[pageSize];
135
136 // reading empty pages in Stack
137 int i = 0;
138 try {
139 while ( true ) {
140 file.seek( 16 + ( i * pageSize ) );
141 if ( EMPTY_PAGE == file.readInt() )
142 emptyPages.push( new Integer( i ) );
143 i++;
144 }
145 } catch ( EOFException eof ) { // not an exception - wanted
146 }
147 } else {
148 // new file
149 file = new RandomAccessFile( fileTest, "rw" );
150 file.setLength( 0 );
151 this.pageSize = ( ( 4 * ( 5 + capacity ) ) + ( ( capacity + 1 ) * ( dimension * 16 ) ) );
152 this.buffer = new byte[pageSize];
153
154 // writing header
155 file.seek( 0 );
156 file.writeInt( PAGEFILE_VERSION );
157 file.writeInt( this.dimension );
158 file.writeInt( this.capacity );
159 file.writeInt( this.minimum );
160
161 }
162 } catch ( IOException e ) {
163 e.fillInStackTrace();
164 throw new PageFileException( "IOException occured: \n " + e.getMessage() );
165 }
166 }
167
168 /**
169 * @see PageFile#readNode(int)
170 */
171 protected Node readNode( int pageFileNumber )
172 throws PageFileException {
173
174 Node node = null;
175
176 try {
177 file.seek( 16 + ( pageFileNumber * pageSize ) );
178
179 int read = file.read( buffer );
180
181 if ( pageSize == read ) {
182
183 DataInputStream ds = new DataInputStream( new ByteArrayInputStream( buffer ) );
184
185 int type = ds.readInt();
186 if ( type == 1 )
187 node = new LeafNode( -1, this );
188 else
189 node = new NoneLeafNode( -1, this );
190
191 node.place = ds.readInt();
192 node.counter = ds.readInt();
193 node.parentNode = ds.readInt();
194 node.pageNumber = ds.readInt();
195
196 if ( type == 1 ) {
197 for ( int i = 0; i < capacity; i++ )
198 ( (LeafNode) node ).data[i] = ds.readInt();
199 } else {
200 for ( int i = 0; i < capacity; i++ )
201 ( (NoneLeafNode) node ).childNodes[i] = ds.readInt();
202 }
203
204 node.unionMinBB = readNextHyperBoundingBox( ds );
205
206 for ( int i = 0; i < capacity; i++ )
207 node.hyperBBs[i] = readNextHyperBoundingBox( ds );
208
209 ds.close();
210 } else {
211 throw new PageFileException( "Exception during read operation" );
212 }
213
214 return node;
215
216 } catch ( IOException e ) {
217 e.fillInStackTrace();
218 throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() );
219 }
220 }
221
222 // reads the next HyperBoundingBox from the byte buffer
223 private HyperBoundingBox readNextHyperBoundingBox( DataInputStream ds )
224 throws IOException {
225
226 double[] point1, point2;
227 point1 = new double[dimension];
228 point2 = new double[dimension];
229
230 for ( int i = 0; i < dimension; i++ )
231 point1[i] = ds.readDouble();
232
233 for ( int i = 0; i < dimension; i++ )
234 point2[i] = ds.readDouble();
235
236 return new HyperBoundingBox( new HyperPoint( point1 ), new HyperPoint( point2 ) );
237 }
238
239 /**
240 * @see PageFile#writeNode(Node)
241 */
242 protected int writeNode( Node node )
243 throws PageFileException {
244 try {
245
246 if ( node.pageNumber < 0 ) {
247 if ( !emptyPages.empty() )
248 node.setPageNumber( emptyPages.pop() );
249 else
250 node.setPageNumber( (int) ( ( file.length() - 16 ) / pageSize ) );
251 }
252
253 ByteArrayOutputStream bs = new ByteArrayOutputStream( pageSize );
254 DataOutputStream ds = new DataOutputStream( bs );
255
256 int type;
257 if ( node instanceof LeafNode )
258 type = 1;
259 else
260 type = 2;
261
262 ds.writeInt( type );
263
264 ds.writeInt( node.place );
265 ds.writeInt( node.counter );
266 ds.writeInt( node.parentNode );
267 ds.writeInt( node.pageNumber );
268
269 if ( node instanceof LeafNode ) {
270 for ( int i = 0; i < node.counter; i++ ) {
271 ds.writeInt( ( (LeafNode) node ).data[i] );
272 }
273 for ( int i = 0; i < ( capacity - node.counter ); i++ )
274 ds.writeInt( -1 );
275 } else {
276 for ( int i = 0; i < node.counter; i++ ) {
277 ds.writeInt( ( (NoneLeafNode) node ).childNodes[i] );
278 }
279
280 for ( int i = 0; i < ( capacity - node.counter ); i++ )
281 ds.writeInt( -1 );
282 }
283
284 for ( int i = 0; i < dimension; i++ )
285 ds.writeDouble( node.unionMinBB.getPMin().getCoord( i ) );
286
287 for ( int i = 0; i < dimension; i++ )
288 ds.writeDouble( node.unionMinBB.getPMax().getCoord( i ) );
289
290 for ( int j = 0; j < node.counter; j++ ) {
291 for ( int i = 0; i < dimension; i++ )
292 ds.writeDouble( node.hyperBBs[j].getPMin().getCoord( i ) );
293
294 for ( int i = 0; i < dimension; i++ )
295 ds.writeDouble( node.hyperBBs[j].getPMax().getCoord( i ) );
296 }
297
298 for ( int j = 0; j < ( capacity - node.counter ); j++ ) {
299 for ( int i = 0; i < ( dimension * 2 ); i++ )
300 ds.writeDouble( -1 );
301 }
302
303 ds.flush();
304 bs.flush();
305
306 file.seek( 16 + ( pageSize * node.pageNumber ) );
307
308 file.write( bs.toByteArray() );
309
310 ds.close();
311
312 return node.pageNumber;
313
314 } catch ( IOException e ) {
315 e.fillInStackTrace();
316 throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() );
317 }
318 }
319
320 /**
321 * @see PageFile#deleteNode(int)
322 */
323 protected Node deleteNode( int pageNumber )
324 throws PageFileException {
325 Node node = this.readNode( pageNumber );
326 try {
327 file.seek( 16 + ( pageSize * node.pageNumber ) );
328 file.writeInt( EMPTY_PAGE );
329 } catch ( IOException e ) {
330 e.fillInStackTrace();
331 throw new PageFileException( "PageFileException occured ! \n " + e.getMessage() );
332 }
333
334 emptyPages.push( new Integer( pageNumber ) );
335 return node;
336 }
337
338 /**
339 * @see PageFile#close()
340 */
341 protected void close()
342 throws PageFileException {
343 try {
344 file.close();
345 } catch ( IOException e ) {
346 e.fillInStackTrace();
347 throw new PageFileException( "PageFileException during close()" );
348 }
349
350 closed = true;
351 }
352
353 protected void finalize()
354 throws Throwable {
355 if ( !closed )
356 file.close();
357
358 super.finalize();
359 }
360 }