001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/quadtree/DBNode.java $
002 /*---------------- FILE HEADER ------------------------------------------
003
004 This file is part of deegree.
005 Copyright (C) 2001-2008 by:
006 EXSE, Department of Geography, University of Bonn
007 http://www.giub.uni-bonn.de/deegree/
008 lat/lon GmbH
009 http://www.lat-lon.de
010
011 This library is free software; you can redistribute it and/or
012 modify it under the terms of the GNU Lesser General Public
013 License as published by the Free Software Foundation; either
014 version 2.1 of the License, or (at your option) any later version.
015
016 This library is distributed in the hope that it will be useful,
017 but WITHOUT ANY WARRANTY; without even the implied warranty of
018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019 Lesser General Public License for more details.
020
021 You should have received a copy of the GNU Lesser General Public
022 License along with this library; if not, write to the Free Software
023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024
025 Contact:
026
027 Andreas Poth
028 lat/lon GmbH
029 Aennchenstr. 19
030 53177 Bonn
031 Germany
032 E-Mail: poth@lat-lon.de
033
034 Prof. Dr. Klaus Greve
035 Department of Geography
036 University of Bonn
037 Meckenheimer Allee 166
038 53115 Bonn
039 Germany
040 E-Mail: greve@giub.uni-bonn.de
041
042 ---------------------------------------------------------------------------*/
043 package org.deegree.io.quadtree;
044
045 import java.security.InvalidParameterException;
046 import java.sql.Connection;
047 import java.sql.PreparedStatement;
048 import java.sql.ResultSet;
049 import java.sql.SQLException;
050 import java.sql.Statement;
051 import java.util.ArrayList;
052 import java.util.List;
053
054 import org.deegree.framework.log.ILogger;
055 import org.deegree.framework.log.LoggerFactory;
056 import org.deegree.io.DBConnectionPool;
057 import org.deegree.io.DBPoolException;
058 import org.deegree.io.JDBCConnection;
059 import org.deegree.io.quadtree.DBQuadtree.SupportedVersions;
060 import org.deegree.model.spatialschema.Envelope;
061 import org.deegree.model.spatialschema.GeometryFactory;
062 import org.deegree.model.spatialschema.Position;
063
064 /**
065 * Represents a node of a {@link DBQuadtree}. Nodes contain items which have a spatial extent corresponding to the
066 * node's position in the quadtree.
067 *
068 *
069 * @version $Revision: 9342 $
070 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
071 * @author last edited by: $Author: apoth $
072 *
073 * @version 1.0. $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
074 *
075 * @since 2.0
076 */
077 class DBNode<T> implements Node<T> {
078
079 private static ILogger LOG = LoggerFactory.getLogger( DBNode.class );
080
081 private String id = null;
082
083 private int level;
084
085 private String[] fk_subnode = new String[4];
086
087 private Envelope envelope = null;
088
089 private JDBCConnection jdbc = null;
090
091 private DBQuadtree<T> qt = null;
092
093 private String indexTable = null;
094
095 private SupportedVersions version;
096
097 private String indexItemTable;
098
099 /**
100 * A constructor which reads the envelope from the database. Using an old Quadtree layout.
101 *
102 * @param id
103 * @param qt
104 * @param indexTable
105 * @param jdbc
106 * @param level
107 * @throws IndexException
108 * if the node with given id could not be read from the db.
109 */
110 // public DBNode( String id, DBQuadtree<T> qt, String indexTable, JDBCConnection jdbc, int level )
111 // throws IndexException {
112 // this( id, null, qt, indexTable, jdbc, level, SupportedVersions.ONE );
113 // }
114 /**
115 *
116 * @param id
117 * @param env
118 * @param qt
119 * @param indexTable
120 * @param jdbc
121 * @param level
122 * @param version
123 * of the quadtree layout to use
124 * @throws IndexException
125 * if the node with given id could not be read from the db.
126 */
127 public DBNode( String id, Envelope env, DBQuadtree<T> qt, String indexTable, JDBCConnection jdbc, int level,
128 SupportedVersions version ) throws IndexException {
129 this.id = id;
130 this.envelope = env;
131 if ( jdbc == null ) {
132 throw new InvalidParameterException( "The JDBCConnection reference parameter 'jdbc' may not be null." );
133 }
134 this.jdbc = jdbc;
135
136 if ( qt == null ) {
137 throw new InvalidParameterException( "The quadtree reference parameter 'qt' may not be null." );
138 }
139 this.qt = qt;
140
141 if ( level < 1 ) {
142 level = 1;
143 }
144 this.level = level;
145 if ( indexTable == null || "".equals( indexTable.trim() ) ) {
146 throw new InvalidParameterException( "The Table reference String 'indexTable' may neither be null nor an empty string." );
147 }
148 this.indexTable = indexTable.trim();
149 this.indexItemTable = this.indexTable + "_ITEM ";
150 this.version = version;
151 if ( !loadNodeFromDB() ) {
152 addNodeToDB();
153 }
154 for ( int i = 0; i < fk_subnode.length; ++i ) {
155 if ( fk_subnode[i] == null || "null".equalsIgnoreCase( fk_subnode[i].trim() ) ) {
156 fk_subnode[i] = "";
157 }
158 }
159 qt.addToCache( this );
160 }
161
162 /**
163 * A constructor which reads the envelope from the database.
164 *
165 * @param id
166 * @param qt
167 * @param indexTable
168 * @param jdbc
169 * @param level
170 * @param version
171 * of the quadtree layout to use.
172 * @throws IndexException
173 * if the node with given id could not be read from the db.
174 */
175 public DBNode( String id, DBQuadtree<T> qt, String indexTable, JDBCConnection jdbc, int level,
176 SupportedVersions version ) throws IndexException {
177 this( id, null, qt, indexTable, jdbc, level, version );
178 }
179
180 public boolean insert( T itemKey, Envelope itemEnv )
181 throws IndexException {
182 if ( version == SupportedVersions.ONE ) {
183 return insertWithLayoutOne( itemKey, itemEnv );
184 } else if ( version == SupportedVersions.TWO ) {
185 return insertWithLayoutTwo( itemKey, itemEnv );
186 }
187 return false;
188 }
189
190 public void deleteRange( Envelope envelope ) {
191 if ( level == qt.getDepth() ) {
192 // TODO delete a range from the bottomlevel
193 } else {
194 // TODO delete a range smaller then the depth
195 }
196 }
197
198 /**
199 * @throws UnsupportedOperationException
200 * if the version of this quadtree(node) is not 2.0.0 or higher.
201 */
202 public boolean delete( T itemKey, Envelope itemEnv )
203 throws IndexException {
204 if ( version != SupportedVersions.TWO ) {
205 String msg = "Deleting of items is only supported for the quadtree structure with version '2.0.0' or higher";
206 LOG.logError( msg );
207 throw new UnsupportedOperationException( msg );
208 }
209 Connection dbConnection = null;
210 DBConnectionPool pool = null;
211 pool = DBConnectionPool.getInstance();
212 try {
213 dbConnection = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
214 } catch ( DBPoolException e ) {
215 throw new IndexException( e );
216 }
217 boolean result = deleteWithLayoutTwo( itemKey, itemEnv, null, dbConnection );
218
219 try {
220 pool.releaseConnection( dbConnection, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
221 } catch ( DBPoolException e ) {
222 LOG.logError( "Could not release the db Connection after deletion in the quadtree because: " + e.getMessage() );
223 }
224 return result;
225
226 }
227
228 public boolean update( T itemKey, Envelope newBBox ) {
229 // nottin yet
230 return true;
231 }
232
233 public List<T> query( Envelope searchEnv, List<T> visitor, int currentLevel )
234 throws IndexException {
235 if ( version == SupportedVersions.ONE ) {
236 LOG.logDebug( "Performing query with layout 1" );
237 queryWithLayoutOne( searchEnv, visitor, currentLevel );
238 } else if ( version == SupportedVersions.TWO ) {
239 LOG.logDebug( "Performing query with layout 2" );
240 queryWithLayoutTwo( searchEnv, visitor );
241 }
242 return visitor;
243 }
244
245 public String getId() {
246 return id;
247 }
248
249 /**
250 * @return the envelope (bbox) of this dbNode.
251 */
252 public Envelope getEnvelope() {
253 return envelope;
254 }
255
256 /**
257 * creates a new node with current ID and envelope
258 *
259 * @throws IndexException
260 */
261 private void addNodeToDB()
262 throws IndexException {
263 Connection con = null;
264 DBConnectionPool pool = null;
265 try {
266 pool = DBConnectionPool.getInstance();
267 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
268
269 StringBuilder sb = new StringBuilder( 100 );
270 sb.append( "INSERT INTO " ).append( indexTable );
271 sb.append( " ( ID, MINX, MINY, MAXX , MAXY ) " );
272 sb.append( "VALUES ( ?, ?, ?, ?, ? ) " );
273 PreparedStatement stmt = con.prepareStatement( sb.toString() );
274 stmt.setString( 1, id );
275 stmt.setFloat( 2, (float) envelope.getMin().getX() );
276 stmt.setFloat( 3, (float) envelope.getMin().getY() );
277 stmt.setFloat( 4, (float) envelope.getMax().getX() );
278 stmt.setFloat( 5, (float) envelope.getMax().getY() );
279 stmt.execute();
280 stmt.close();
281 } catch ( Exception e ) {
282 LOG.logError( e.getMessage(), e );
283 throw new IndexException( "could not create node definition at database", e );
284 } finally {
285 try {
286 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
287 } catch ( Exception e1 ) {
288 e1.printStackTrace();
289 }
290 }
291 }
292
293 /**
294 * assignes an item to a node by creating a new row in the JT_QTNODE_ITEM table
295 *
296 * @param Item
297 */
298 private void assignItem( T itemKey )
299 throws IndexException {
300 Connection con = null;
301 DBConnectionPool pool = null;
302 try {
303 pool = DBConnectionPool.getInstance();
304 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
305
306 StringBuilder sb = new StringBuilder( 100 );
307 sb.append( "INSERT INTO " ).append( indexItemTable );
308 sb.append( "( FK_QTNODE, FK_ITEM ) " ).append( "VALUES ( ?, ? ) " );
309 PreparedStatement stmt = con.prepareStatement( sb.toString() );
310 stmt.setString( 1, id );
311 if ( itemKey instanceof Integer ) {
312 stmt.setInt( 2, ( (Integer) itemKey ).intValue() );
313 } else {
314 stmt.setString( 2, itemKey.toString() );
315 }
316 stmt.execute();
317 stmt.close();
318 } catch ( Exception e ) {
319 LOG.logError( e.getMessage(), e );
320 throw new IndexException( "could not create node definition at database", e );
321 } finally {
322 try {
323 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
324 } catch ( Exception e1 ) {
325 e1.printStackTrace();
326 }
327 }
328 }
329
330 /**
331 * This method inserts the given item into the quadtree. The difference between layout ONE and TWO is, that in
332 * version TWO all nodes which lie inside the item's bbox possess a reference to the item, allthough the idx_table
333 * will be larger, the retrieval of multiple items inside a requested bbox will be a lot faster.
334 *
335 * @param itemKey
336 * to be inserted
337 * @param itemEnv
338 * bbox of the item to be inserted.
339 * @return true if the item was successfully inserted into the quadtree false otherwise.
340 * @throws IndexException
341 * if the item could not be inserted because of an <code>db</code> or <code>sql</code> error.
342 */
343 private boolean insertWithLayoutTwo( T itemKey, Envelope itemEnv )
344 throws IndexException {
345 if ( level != qt.getDepth() ) {
346 if ( !envelope.intersects( itemEnv ) ) {
347 String msg = "The item's envelope: " + itemEnv
348 + " does not intersect with the quadtrees' boundinbox envelope: "
349 + envelope;
350 LOG.logError( msg );
351 throw new IndexException( msg );
352 }
353 // split the envelope of this node into four equal sized quarters
354 Envelope[] envs = split();
355 boolean needsUpdate = false;
356 boolean nodeInserted = false;
357 for ( int i = 0; i < envs.length; i++ ) {
358 if ( envs[i].intersects( itemEnv ) ) {
359 // check which subnodes are intersected by the
360 // items envelope; only these nodes are considered for futher processing
361 if ( "".equals( fk_subnode[i].trim() ) ) {
362 needsUpdate = true;
363 fk_subnode[i] = id + '_' + i;
364 }
365 DBNode<T> node = qt.getFromCache( fk_subnode[i] );
366 if ( node == null ) {
367 node = new DBNode<T>( fk_subnode[i], envs[i], qt, indexTable, jdbc, level + 1, version );
368 }
369 nodeInserted = node.insertWithLayoutTwo( itemKey, itemEnv );
370 }
371 }
372 // if any child node inserted the item, the parent must know about it too, this enhances the speed of area
373 // querying.
374 if ( nodeInserted ) {
375 assignItem( itemKey );
376 }
377 if ( needsUpdate ) {
378 updateNodeInDB();
379 }
380 return nodeInserted;
381 }
382 assignItem( itemKey );
383 return true;
384 }
385
386 /**
387 * @param itemEnv
388 * the items envelope
389 * @return true if this nodes envelope lies completely inside the items envelope.
390 */
391 private boolean liesWithIn( Envelope itemEnv ) {
392 Position minThis = envelope.getMin();
393 Position maxThis = envelope.getMax();
394 Position minThat = itemEnv.getMin();
395 Position maxThat = itemEnv.getMax();
396 return ( minThis.getX() >= minThat.getX() && maxThis.getX() <= maxThat.getX()
397 && minThis.getY() > minThat.getY() && maxThis.getY() < maxThat.getY() );
398
399 }
400
401 private boolean insertWithLayoutOne( T itemKey, Envelope itemEnv )
402 throws IndexException {
403 if ( level != qt.getDepth() ) {
404 if ( !envelope.intersects( itemEnv ) ) {
405 String msg = "The item's envelope: " + itemEnv
406 + " does not intersect with the quadtrees' boundinbox envelope: "
407 + envelope;
408 LOG.logError( msg );
409 throw new IndexException( msg );
410 }
411 // split the envelope of this node into four equal sized quarters
412 Envelope[] envs = split();
413 boolean needsUpdate = false;
414 int numberOfInsertedSons = 0;
415 for ( int i = 0; i < envs.length; i++ ) {
416 if ( envs[i].intersects( itemEnv ) ) {
417 numberOfInsertedSons++;
418 // check which subnodes are intersected by the
419 // items envelope; only these nodes are considered for futher processing
420 if ( "".equals( fk_subnode[i].trim() ) ) {
421 needsUpdate = true;
422 fk_subnode[i] = id + '_' + i;
423 }
424 DBNode<T> node = qt.getFromCache( fk_subnode[i] );
425 if ( node == null ) {
426 node = new DBNode<T>( fk_subnode[i], envs[i], qt, indexTable, jdbc, level + 1, version );
427 }
428 node.insertWithLayoutOne( itemKey, itemEnv );
429 }
430 }
431 if ( numberOfInsertedSons == 4 ) {
432 assignItem( itemKey );
433 }
434 if ( needsUpdate ) {
435 updateNodeInDB();
436 }
437 } else {
438 assignItem( itemKey );
439 }
440 return true;
441 }
442
443 /**
444 * The enhancement of the second layout is, that every node knows about the items inserted in it's sons. This
445 * information can be used if the bbox of this node lies totally within the requested area, all items of the sons of
446 * this node are added automatically.
447 *
448 * @param searchEnv
449 * the area to get all items for.
450 * @param visitor
451 * the list inwhich the items keys will be added.
452 * @param currentLevel
453 * of the tree
454 * @throws IndexException
455 * if a connection to the db failed.
456 */
457 private void queryWithLayoutTwo( Envelope searchEnv, List<T> visitor )
458 throws IndexException {
459 if ( liesWithIn( searchEnv ) || level == qt.getDepth() ) {
460 getAssignedItems( visitor );
461 } else {
462 Envelope[] envs = split();
463 for ( int i = 0; i < envs.length; i++ ) {
464 if ( !"".equals( fk_subnode[i] ) && envs[i].intersects( searchEnv ) ) {
465 // check which subnodes are intersected by the
466 // items envelope; just this nodes
467 // are considered for futher processing
468 DBNode<T> node = new DBNode<T>( fk_subnode[i], envs[i], qt, indexTable, jdbc, level + 1, version );
469 node.queryWithLayoutTwo( searchEnv, visitor );
470 }
471 }
472 }
473
474 }
475
476 private void queryWithLayoutOne( Envelope searchEnv, List<T> visitor, int level )
477 throws IndexException {
478 /*
479 * if ( level == qt.getDepth() || (searchEnv.getWidth() > envelope.getWidth() || searchEnv.getHeight() >
480 * envelope.getHeight()) ) { addAssignedItems( visitor ); } else {
481 */
482 getAssignedItems( visitor );
483 if ( level != qt.getDepth() ) {
484 Envelope[] envs = split();
485 for ( int i = 0; i < envs.length; i++ ) {
486 if ( !"".equals( fk_subnode[i] ) && envs[i].intersects( searchEnv ) ) {
487 // check which subnodes are intersected by the
488 // items envelope; just this nodes
489 // are considered for futher processing
490 DBNode<T> node = new DBNode<T>( fk_subnode[i], envs[i], qt, indexTable, jdbc, level + 1, version );
491 node.queryWithLayoutOne( searchEnv, visitor, level + 1 );
492 }
493 }
494 }
495 }
496
497 /**
498 * The delete method which can handle the layout with version two.
499 *
500 * @param itemKey
501 * the key to be deleted.
502 * @param itemEnv
503 * the bbox of the item.
504 * @param parent
505 * the parent node of this qt-node or <code>null</code> if this is the root node.
506 * @throws IndexException
507 * if somehow the connection to the db is lost or an error occurred while executing the deletion.
508 */
509 private boolean deleteWithLayoutTwo( T itemKey, Envelope itemEnv, DBNode<T> parent, Connection dbConnection )
510 throws IndexException {
511 List<T> items = new ArrayList<T>();
512 // first get all items assigned to this node, and remove the fitting item from the idx-item join table.
513 getAssignedItems( items );
514 if ( items.contains( itemKey ) ) {
515 if ( deleteItemFromDB( itemKey, dbConnection ) ) {
516 items.remove( itemKey );
517 }
518 }
519 if ( level != qt.getDepth() ) {
520 Envelope[] envs = split();
521 for ( int i = 0; i < fk_subnode.length; ++i ) {
522 if ( !"".equals( fk_subnode[i] ) && envs[i].intersects( itemEnv ) ) {
523 DBNode<T> node = new DBNode<T>( fk_subnode[i], envs[i], qt, indexTable, jdbc, level + 1, version );
524 node.deleteWithLayoutTwo( itemKey, itemEnv, this, dbConnection );
525 }
526 }
527 }
528 // If this is not the root of the quadtree, delete this node from the parent if it is a leaf and has no items.
529 if ( parent != null ) {
530 if ( items.isEmpty() && !hasSons() ) {
531 if ( deletNodeFromDB( dbConnection ) ) {
532 if ( parent.deleteSon( id, dbConnection ) ) {
533 qt.addToCache( parent );
534 qt.removeFromCache( this );
535 }
536 }
537 }
538 }
539 return true;
540 }
541
542 /**
543 * @param dbConnection
544 * a live connection to the database
545 * @return true if the node has been successfully removed from the database.
546 */
547 private boolean deletNodeFromDB( Connection dbConnection ) {
548 if ( hasSons() ) {
549 LOG.logError( "Trying to delete a node (with id: '" + id + "') which still has sons, this may not happen!" );
550 return false;
551 }
552 StringBuilder deleteStatement = new StringBuilder( 400 );
553 deleteStatement.append( "DELETE FROM " ).append( indexTable );
554 deleteStatement.append( " WHERE ID = '" ).append( id ).append( "'" );
555 LOG.logDebug( "Trying to delete dbnode with id='" + id
556 + "'. The sql statement is as follows:\n"
557 + deleteStatement.toString() );
558 Statement stmt = null;
559 ResultSet rs = null;
560 try {
561 stmt = dbConnection.createStatement();
562 rs = stmt.executeQuery( deleteStatement.toString() );
563 } catch ( SQLException e ) {
564 LOG.logDebug( "Could not delete the dbNode with id='" + id + "' because: " + e.getMessage() );
565 return false;
566 } finally {
567 try {
568 if ( rs != null ) {
569 rs.close();
570 }
571 if ( stmt != null ) {
572 stmt.close();
573 }
574 } catch ( SQLException e ) {
575 LOG.logDebug( "An error occurred while trying to close statement and/or resultset because: " + e.getMessage() );
576 }
577
578 }
579 return true;
580 }
581
582 /**
583 * @return true if this node has a son (e.g. one of the fk_subnodes is not empty ).
584 */
585 private boolean hasSons() {
586 return !( "".equals( fk_subnode[0] ) && "".equals( fk_subnode[1] ) && "".equals( fk_subnode[2] ) && "".equals( fk_subnode[3] ) );
587
588 }
589
590 /**
591 * Deletes the specifies item from the quadtree, e.g. removing it from the idx_table_item join table, where the
592 * FK_QTNODE equals this.id and the FK_ITEM = itemKey.
593 *
594 * @param itemKey
595 * to be deleted
596 * @param dbConnection
597 * used to execute the query.
598 * @return true if the item was deleted from the database, false otherwhise.
599 */
600 private boolean deleteItemFromDB( T itemKey, Connection dbConnection ) {
601 if ( itemKey == null ) {
602 LOG.logDebug( "Trying to delete an itemkey which is null, this may not be (current node id='" + id + "')" );
603 return false;
604 }
605 StringBuilder delItemStatement = new StringBuilder( 400 );
606 delItemStatement.append( "DELETE FROM " ).append( indexItemTable );
607 delItemStatement.append( " WHERE FK_QTNODE = '" ).append( id ).append( "'" );
608 delItemStatement.append( " AND FK_ITEM = " );
609 if ( itemKey instanceof String ) {
610 delItemStatement.append( "'" );
611 }
612 delItemStatement.append( itemKey );
613 if ( itemKey instanceof String ) {
614 delItemStatement.append( "'" );
615 }
616 LOG.logDebug( "Trying to delete item with key='" + itemKey
617 + "' from node with id='"
618 + id
619 + "'. The sql statement is as follows:\n"
620 + delItemStatement.toString() );
621 Statement stmt = null;
622 ResultSet rs = null;
623 try {
624 stmt = dbConnection.createStatement();
625 rs = stmt.executeQuery( delItemStatement.toString() );
626 } catch ( SQLException e ) {
627 LOG.logDebug( "Could not delete the item with key='" + itemKey
628 + "' from node with id='"
629 + id
630 + "' because: "
631 + e.getMessage() );
632 return false;
633 } finally {
634 try {
635 if ( rs != null ) {
636 rs.close();
637 }
638 if ( stmt != null ) {
639 stmt.close();
640 }
641 } catch ( SQLException e ) {
642 LOG.logDebug( "An error occurred while trying to close statement and/or resultset because: " + e.getMessage() );
643 }
644
645 }
646
647 return true;
648
649 }
650
651 /**
652 * Deletes a son from the database, e.g. perform an update to null on the idx_table. And if successfull also sets
653 * the fk_subnode to an empty string.
654 *
655 * @param sonsID
656 * which must be set to null.
657 * @param dbConnection
658 * which will be used to perform the query.
659 * @return true if the son was deleted.
660 */
661 private boolean deleteSon( String sonsID, Connection dbConnection ) {
662 if ( sonsID == null || "".equals( sonsID.trim() ) ) {
663 LOG.logDebug( "Trying to delete a son with an id which is null, this may not be (parent node id='" + id
664 + "')" );
665 return false;
666 }
667 for ( int i = 0; i < fk_subnode.length; ++i ) {
668 if ( fk_subnode[i].trim().equals( sonsID.trim() ) ) {
669 StringBuilder delSonStatement = new StringBuilder( 400 );
670 delSonStatement.append( "UPDATE " ).append( indexTable );
671 delSonStatement.append( " SET FK_SUBNODE" ).append( ( i + 1 ) ).append( " = 'null'" );
672 delSonStatement.append( " WHERE ID = '" ).append( id ).append( "'" );
673
674 LOG.logDebug( "Trying to delete son with id='" + sonsID
675 + "' from (parent) node with id='"
676 + this.id
677 + "'. The sql statement is as follows:\n"
678 + delSonStatement.toString() );
679 Statement stmt = null;
680 ResultSet rs = null;
681 try {
682 stmt = dbConnection.createStatement();
683 rs = stmt.executeQuery( delSonStatement.toString() );
684 } catch ( SQLException e ) {
685 LOG.logDebug( "Could not delete son with id='" + sonsID
686 + "' from (parent) node with id='"
687 + this.id
688 + "' because: "
689 + e.getMessage() );
690 return false;
691 } finally {
692 try {
693 if ( rs != null ) {
694 rs.close();
695 }
696 if ( stmt != null ) {
697 stmt.close();
698 }
699 } catch ( SQLException e ) {
700 LOG.logDebug( "An error occurred while trying to close statement and/or resultset because: " + e.getMessage() );
701 }
702
703 }
704 fk_subnode[i] = "";
705 return true;
706 }
707
708 }
709
710 LOG.logDebug( "It seems this (parent) node with id='" + id
711 + "' has no son with id='"
712 + sonsID
713 + "', all sons of this node are:\n "
714 + "- fk_subnode[0]='"
715 + fk_subnode[0]
716 + "'\n "
717 + "- fk_subnode[1]='"
718 + fk_subnode[1]
719 + "'\n "
720 + "- fk_subnode[2]='"
721 + fk_subnode[2]
722 + "'\n "
723 + "- fk_subnode[3]='"
724 + fk_subnode[3]
725 + "'" );
726 return false;
727 }
728
729 /**
730 * load all Node parameters from the database.
731 *
732 * @return true if a node with current ID is already available from the database and the parameters have been read
733 * successfully.
734 *
735 */
736 private boolean loadNodeFromDB()
737 throws IndexException {
738 Connection con = null;
739 DBConnectionPool pool = null;
740 boolean available = true;
741 try {
742 pool = DBConnectionPool.getInstance();
743 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
744
745 StringBuilder sb = new StringBuilder( 100 );
746 sb.append( "Select * from " ).append( indexTable );
747 sb.append( " where ID = '" ).append( id ).append( "'" );
748
749 Statement stmt = con.createStatement();
750 ResultSet rs = stmt.executeQuery( sb.toString() );
751 if ( rs.next() ) {
752 double minx = rs.getFloat( "MINX" );
753 double miny = rs.getFloat( "MINY" );
754 double maxx = rs.getFloat( "MAXX" );
755 double maxy = rs.getFloat( "MAXY" );
756 envelope = GeometryFactory.createEnvelope( minx, miny, maxx, maxy, null );
757 fk_subnode[0] = rs.getString( "FK_SUBNODE1" );
758 fk_subnode[1] = rs.getString( "FK_SUBNODE2" );
759 fk_subnode[2] = rs.getString( "FK_SUBNODE3" );
760 fk_subnode[3] = rs.getString( "FK_SUBNODE4" );
761 } else {
762 available = false;
763 }
764 rs.close();
765 stmt.close();
766 } catch ( Exception e ) {
767 LOG.logError( e.getMessage(), e );
768 throw new IndexException( "could not load node definition from database", e );
769 } finally {
770 try {
771 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
772 } catch ( Exception e1 ) {
773 e1.printStackTrace();
774 }
775 }
776 return available;
777 }
778
779 /**
780 * updates the database representation of the current node
781 *
782 * @throws IndexException
783 */
784 private void updateNodeInDB()
785 throws IndexException {
786 Connection con = null;
787 DBConnectionPool pool = null;
788 try {
789 pool = DBConnectionPool.getInstance();
790 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
791
792 StringBuilder sb = new StringBuilder( 250 );
793 sb.append( "UPDATE " ).append( indexTable ).append( " set " );
794 boolean submit = false;
795 for ( int i = 0; i < fk_subnode.length; i++ ) {
796 if ( !"".equals( fk_subnode[i] ) ) {
797 sb.append( " FK_SUBNODE" ).append( i + 1 ).append( "='" );
798 sb.append( fk_subnode[i] ).append( "' ," );
799 submit = true;
800 }
801 }
802 if ( submit ) {
803 // just execute update if at least one sub node != null
804 sb = new StringBuilder( sb.substring( 0, sb.length() - 1 ) );
805 sb.append( " where ID = '" ).append( id ).append( "'" );
806 Statement stmt = con.createStatement();
807 stmt.execute( sb.toString() );
808 stmt.close();
809 }
810 } catch ( Exception e ) {
811 LOG.logError( e.getMessage(), e );
812 throw new IndexException( "could not update node definition at database " + "for node: " + id, e );
813 } finally {
814 try {
815 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
816 } catch ( Exception e1 ) {
817 e1.printStackTrace();
818 }
819 }
820 qt.addToCache( this );
821 }
822
823 /**
824 * Get the item (IDs) assigned to this node and stores them in the given list
825 *
826 * @param visitor
827 * @throws IndexException
828 */
829 private void getAssignedItems( List<T> visitor )
830 throws IndexException {
831
832 Connection con = null;
833 DBConnectionPool pool = null;
834 try {
835 pool = DBConnectionPool.getInstance();
836 con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
837
838 StringBuilder sb = new StringBuilder( 100 );
839 sb.append( "SELECT DISTINCT FK_ITEM from " ).append( indexTable ).append( "_ITEM" );
840 sb.append( " where " ).append( "FK_QTNODE = '" ).append( id ).append( "'" );
841 Statement stmt = con.createStatement();
842 ResultSet rs = stmt.executeQuery( sb.toString() );
843
844 while ( rs.next() ) {
845 Object result = rs.getObject( 1 );
846 if ( result != null ) {
847 T s = (T) result;
848 if ( !visitor.contains( s ) ) {
849 visitor.add( s );
850 }
851 } else {
852 LOG.logDebug( "Found a node (id='" + id + "') with a null value." );
853 }
854 }
855 stmt.close();
856 } catch ( DBPoolException e ) {
857 throw new IndexException( "Database QuadTree could not acquire sons of a node because: " + e.getMessage() );
858 } catch ( SQLException e ) {
859 throw new IndexException( "Database QuadTree could not acquire sons of a node because: " + e.getMessage() );
860 } finally {
861 try {
862 pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
863 } catch ( Exception e1 ) {
864 e1.printStackTrace();
865 }
866 }
867 }
868
869 private Envelope[] split() {
870 Envelope[] envs = new Envelope[4];
871 double nW = envelope.getWidth() / 2d;
872 double nH = envelope.getHeight() / 2d;
873
874 envs[0] = GeometryFactory.createEnvelope( envelope.getMin().getX(),
875 envelope.getMin().getY(),
876 envelope.getMin().getX() + nW,
877 envelope.getMin().getY() + nH,
878 null );
879 envs[1] = GeometryFactory.createEnvelope( envelope.getMin().getX() + nW,
880 envelope.getMin().getY(),
881 envelope.getMin().getX() + ( 2 * nW ),
882 envelope.getMin().getY() + nH,
883 null );
884 envs[2] = GeometryFactory.createEnvelope( envelope.getMin().getX() + nW,
885 envelope.getMin().getY() + nH,
886 envelope.getMin().getX() + ( 2 * nW ),
887 envelope.getMin().getY() + ( 2 * nH ),
888 null );
889 envs[3] = GeometryFactory.createEnvelope( envelope.getMin().getX(),
890 envelope.getMin().getY() + nH,
891 envelope.getMin().getX() + nW,
892 envelope.getMin().getY() + ( 2 * nH ),
893 null );
894
895 return envs;
896 }
897
898 // /**
899 // * @param itemKey
900 // * the key of the item to get the bbox for.
901 // * @return the envelope of item with itemKey, currently stored in the quadtree.
902 // */
903 // public Envelope getBoundingBoxForItem( T itemKey ) {
904 // return null;
905 // }
906
907 }