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