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 }