001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/datastore/sql/transaction/delete/DeleteHandler.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2006 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 Aennchenstraße 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.datastore.sql.transaction.delete; 044 045 import java.sql.Connection; 046 import java.sql.PreparedStatement; 047 import java.sql.ResultSet; 048 import java.sql.SQLException; 049 import java.util.ArrayList; 050 import java.util.Collection; 051 import java.util.List; 052 053 import org.deegree.datatypes.Types; 054 import org.deegree.framework.log.ILogger; 055 import org.deegree.framework.log.LoggerFactory; 056 import org.deegree.i18n.Messages; 057 import org.deegree.io.datastore.Datastore; 058 import org.deegree.io.datastore.DatastoreException; 059 import org.deegree.io.datastore.FeatureId; 060 import org.deegree.io.datastore.schema.MappedFeaturePropertyType; 061 import org.deegree.io.datastore.schema.MappedFeatureType; 062 import org.deegree.io.datastore.schema.MappedGMLSchema; 063 import org.deegree.io.datastore.schema.MappedPropertyType; 064 import org.deegree.io.datastore.schema.TableRelation; 065 import org.deegree.io.datastore.schema.content.MappingField; 066 import org.deegree.io.datastore.sql.AbstractRequestHandler; 067 import org.deegree.io.datastore.sql.StatementBuffer; 068 import org.deegree.io.datastore.sql.TableAliasGenerator; 069 import org.deegree.io.datastore.sql.transaction.SQLTransaction; 070 import org.deegree.io.datastore.sql.transaction.UpdateHandler; 071 import org.deegree.model.feature.schema.FeatureType; 072 import org.deegree.model.feature.schema.PropertyType; 073 import org.deegree.model.filterencoding.Filter; 074 import org.deegree.ogcwebservices.wfs.operation.transaction.Delete; 075 import org.deegree.ogcwebservices.wfs.operation.transaction.Transaction; 076 077 /** 078 * Handler for {@link Delete} operations (which usually occur as parts of {@link Transaction} 079 * requests). 080 * <p> 081 * When a {@link Delete} operation is performed, the following actions are taken: 082 * <ul> 083 * <li>the {@link FeatureId}s of all (root) feature instances that match the associated 084 * {@link Filter} are determined</li> 085 * <li>the {@link FeatureGraph} is built in order to determine which features may be deleted 086 * without removing subfeatures of independent features</li> 087 * <li>the {@link TableGraph} is built that contains explicit information on all table rows that 088 * have to be deleted (and their dependencies)</li> 089 * <li>the {@link TableNode}s of the {@link TableGraph} are sorted in topological order, i.e. they 090 * may be deleted in that order without violating any foreign key constraints</li> 091 * </ul> 092 * 093 * @see FeatureGraph 094 * @see TableGraph 095 * 096 * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider</a> 097 * @author last edited by: $Author: mschneider $ 098 * 099 * @version $Revision: 7737 $, $Date: 2007-07-09 14:09:59 +0200 (Mo, 09 Jul 2007) $ 100 */ 101 public class DeleteHandler extends AbstractRequestHandler { 102 103 private static final ILogger LOG = LoggerFactory.getLogger( DeleteHandler.class ); 104 105 private String lockId; 106 107 /** 108 * Creates a new <code>DeleteHandler</code> from the given parameters. 109 * 110 * @param dsTa 111 * @param aliasGenerator 112 * @param conn 113 * @param lockId 114 * optional id of associated lock (may be null) 115 */ 116 public DeleteHandler( SQLTransaction dsTa, TableAliasGenerator aliasGenerator, Connection conn, 117 String lockId ) { 118 super( dsTa.getDatastore(), aliasGenerator, conn ); 119 this.lockId = lockId; 120 } 121 122 /** 123 * Deletes the features from the {@link Datastore} that have a certain type and are matched by 124 * the given filter. 125 * 126 * @param ft 127 * non-abstract feature type of the features to be deleted 128 * @param filter 129 * constraints the feature instances to be deleted 130 * @return number of deleted feature instances 131 * @throws DatastoreException 132 */ 133 public int performDelete( MappedFeatureType ft, Filter filter ) 134 throws DatastoreException { 135 136 assert !ft.isAbstract(); 137 138 if ( !ft.isDeletable() ) { 139 String msg = Messages.getMessage( "DATASTORE_FT_NOT_DELETABLE", ft.getName() ); 140 throw new DatastoreException( msg ); 141 } 142 143 List<FeatureId> fids = determineAffectedAndModifiableFIDs( ft, filter, this.lockId ); 144 145 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 146 LOG.logDebug( "Affected fids:" ); 147 for ( FeatureId fid : fids ) { 148 LOG.logDebug( "" + fid ); 149 } 150 } 151 152 FeatureGraph featureGraph = new FeatureGraph( fids, this ); 153 TableGraph tableGraph = new TableGraph( featureGraph, this ); 154 155 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) { 156 LOG.logDebug( "FeatureGraph: " + featureGraph ); 157 LOG.logDebug( "TableGraph: " + tableGraph ); 158 } 159 160 List<TableNode> sortedNodes = tableGraph.getNodesInTopologicalOrder(); 161 for ( TableNode node : sortedNodes ) { 162 boolean delete = true; 163 if ( node.isDeleteVetoPossible() ) { 164 List<TableNode> referencingRows = getReferencingRows( node ); 165 if ( referencingRows.size() > 0 ) { 166 delete = false; 167 LOG.logDebug( "Skipping delete of " + node + ": " + referencingRows.size() 168 + " reference(s) exist." ); 169 for ( TableNode referencingNode : referencingRows ) { 170 LOG.logDebug( "Referenced by: " + referencingNode ); 171 } 172 } 173 } 174 if ( delete ) { 175 performDelete( node ); 176 } 177 } 178 179 int deletedFeatures = tableGraph.getDeletableRootFeatureCount(); 180 181 if ( deletedFeatures != fids.size() ) { 182 String msg = Messages.getMessage( "DATASTORE_COULD_NOT_DELETE_ALL" ); 183 LOG.logInfo( msg ); 184 } 185 186 // return count of actually deleted (root) features 187 return deletedFeatures; 188 } 189 190 /** 191 * Deletes the table entry from the SQL database that is represented by the given 192 * {@link TableNode}. 193 * 194 * @param node 195 * @throws DatastoreException 196 */ 197 private void performDelete( TableNode node ) 198 throws DatastoreException { 199 200 StatementBuffer query = new StatementBuffer(); 201 query.append( "DELETE FROM " ); 202 query.append( node.getTable() ); 203 query.append( " WHERE " ); 204 boolean first = true; 205 for ( KeyColumn column : node.getKeyColumns() ) { 206 if ( first ) { 207 first = false; 208 } else { 209 query.append( " AND " ); 210 } 211 query.append( column.getName() ); 212 query.append( "=?" ); 213 query.addArgument( column.getValue(), column.getTypeCode() ); 214 } 215 216 PreparedStatement stmt = null; 217 try { 218 stmt = this.datastore.prepareStatement( conn, query ); 219 LOG.logDebug( "Deleting row: " + query ); 220 stmt.execute(); 221 } catch ( SQLException e ) { 222 String msg = "Error performing delete '" + query + "': " + e.getMessage(); 223 LOG.logInfo( msg, e ); 224 throw new DatastoreException( msg ); 225 } finally { 226 if ( stmt != null ) { 227 try { 228 stmt.close(); 229 } catch ( SQLException e ) { 230 String msg = "Error closing statement: " + e.getMessage(); 231 LOG.logError( msg, e ); 232 } 233 } 234 } 235 } 236 237 /** 238 * Adds nodes to the given {@link TableNode} that represent the simple/geometry properties in 239 * the property table attached by the given {@link TableRelation}. 240 * 241 * @param fid 242 * id of the feature that owns the properties 243 * @param relation 244 * describes how the property table is joined to the feature table 245 * @throws DatastoreException 246 */ 247 List<TableNode> determinePropNodes( FeatureId fid, TableRelation relation ) 248 throws DatastoreException { 249 250 List<TableNode> propEntries = new ArrayList<TableNode>(); 251 252 this.aliasGenerator.reset(); 253 String fromAlias = this.aliasGenerator.generateUniqueAlias(); 254 String toAlias = this.aliasGenerator.generateUniqueAlias(); 255 MappingField[] fromFields = relation.getFromFields(); 256 MappingField[] toFields = relation.getToFields(); 257 258 StatementBuffer query = new StatementBuffer(); 259 query.append( "SELECT DISTINCT " ); 260 for ( int i = 0; i < toFields.length; i++ ) { 261 query.append( toAlias ); 262 query.append( "." ); 263 query.append( toFields[i].getField() ); 264 if ( i != toFields.length - 1 ) { 265 query.append( ',' ); 266 } 267 } 268 query.append( " FROM " ); 269 query.append( fid.getFeatureType().getTable() ); 270 query.append( " " ); 271 query.append( fromAlias ); 272 query.append( " INNER JOIN " ); 273 query.append( relation.getToTable() ); 274 query.append( " " ); 275 query.append( toAlias ); 276 query.append( " ON " ); 277 for ( int j = 0; j < fromFields.length; j++ ) { 278 query.append( fromAlias ); 279 query.append( '.' ); 280 query.append( fromFields[j].getField() ); 281 query.append( '=' ); 282 query.append( toAlias ); 283 query.append( '.' ); 284 query.append( toFields[j].getField() ); 285 } 286 query.append( " WHERE " ); 287 appendFeatureIdConstraint( query, fid, fromAlias ); 288 289 PreparedStatement stmt = null; 290 ResultSet rs = null; 291 try { 292 stmt = this.datastore.prepareStatement( conn, query ); 293 LOG.logDebug( "Performing: " + query ); 294 rs = stmt.executeQuery(); 295 while ( rs.next() ) { 296 Collection<KeyColumn> keyColumns = new ArrayList<KeyColumn>(); 297 for ( int i = 0; i < toFields.length; i++ ) { 298 KeyColumn column = new KeyColumn( toFields[i].getField(), 299 toFields[i].getType(), rs.getObject( i + 1 ) ); 300 keyColumns.add( column ); 301 } 302 TableNode propEntry = new TableNode( relation.getToTable(), keyColumns ); 303 propEntries.add( propEntry ); 304 } 305 } catch ( SQLException e ) { 306 LOG.logInfo( e.getMessage(), e ); 307 throw new DatastoreException( "Error in addPropertyNodes(): " + e.getMessage() ); 308 } finally { 309 try { 310 if ( rs != null ) { 311 try { 312 rs.close(); 313 } catch ( SQLException e ) { 314 LOG.logError( "Error closing result set: '" + e.getMessage() + "'.", e ); 315 } 316 } 317 } finally { 318 if ( stmt != null ) { 319 try { 320 stmt.close(); 321 } catch ( SQLException e ) { 322 LOG.logError( "Error closing statement: '" + e.getMessage() + "'.", e ); 323 } 324 } 325 } 326 } 327 return propEntries; 328 } 329 330 /** 331 * Determines the row in the join table that connects a certain feature with a subfeature. 332 * 333 * @param fid 334 * id of the (super-) feature 335 * @param subFid 336 * id of the subfeature 337 * @param relation1 338 * describes how the join table is attached 339 * @param relation2 340 * describes how the subfeature table is joined 341 * @throws DatastoreException 342 */ 343 TableNode determineJTNode( FeatureId fid, FeatureId subFid, TableRelation relation1, 344 TableRelation relation2 ) 345 throws DatastoreException { 346 347 LOG.logDebug( "Determining join table entry for feature " + fid + " and subfeature " 348 + subFid ); 349 TableNode jtEntry = null; 350 351 this.aliasGenerator.reset(); 352 353 String featureTableAlias = this.aliasGenerator.generateUniqueAlias(); 354 String joinTableAlias = this.aliasGenerator.generateUniqueAlias(); 355 String subFeatureTableAlias = this.aliasGenerator.generateUniqueAlias(); 356 357 MappingField[] fromFields = relation1.getFromFields(); 358 MappingField[] fromFields2 = relation2.getFromFields(); 359 MappingField[] toFields = relation1.getToFields(); 360 MappingField[] toFields2 = relation2.getToFields(); 361 362 // need to select 'from' fields of second relation element as well 363 MappingField[] selectFields = new MappingField[toFields.length + fromFields2.length]; 364 for ( int i = 0; i < toFields.length; i++ ) { 365 selectFields[i] = toFields[i]; 366 } 367 for ( int i = 0; i < fromFields2.length; i++ ) { 368 selectFields[i + toFields.length] = fromFields2[i]; 369 } 370 371 StatementBuffer query = new StatementBuffer(); 372 query.append( "SELECT DISTINCT " ); 373 for ( int i = 0; i < selectFields.length; i++ ) { 374 query.append( joinTableAlias ); 375 query.append( "." ); 376 query.append( selectFields[i].getField() ); 377 if ( i != selectFields.length - 1 ) { 378 query.append( ',' ); 379 } 380 } 381 query.append( " FROM " ); 382 query.append( fid.getFeatureType().getTable() ); 383 query.append( " " ); 384 query.append( featureTableAlias ); 385 query.append( " INNER JOIN " ); 386 query.append( relation1.getToTable() ); 387 query.append( " " ); 388 query.append( joinTableAlias ); 389 query.append( " ON " ); 390 for ( int j = 0; j < fromFields.length; j++ ) { 391 query.append( featureTableAlias ); 392 query.append( '.' ); 393 query.append( fromFields[j].getField() ); 394 query.append( '=' ); 395 query.append( joinTableAlias ); 396 query.append( '.' ); 397 query.append( toFields[j].getField() ); 398 } 399 query.append( " INNER JOIN " ); 400 query.append( subFid.getFeatureType().getTable() ); 401 query.append( " " ); 402 query.append( subFeatureTableAlias ); 403 query.append( " ON " ); 404 for ( int j = 0; j < fromFields2.length; j++ ) { 405 query.append( joinTableAlias ); 406 query.append( '.' ); 407 query.append( fromFields2[j].getField() ); 408 query.append( '=' ); 409 query.append( subFeatureTableAlias ); 410 query.append( '.' ); 411 query.append( toFields2[j].getField() ); 412 } 413 414 query.append( " WHERE " ); 415 appendFeatureIdConstraint( query, fid, featureTableAlias ); 416 query.append( " AND " ); 417 appendFeatureIdConstraint( query, subFid, subFeatureTableAlias ); 418 419 PreparedStatement stmt = null; 420 ResultSet rs = null; 421 try { 422 stmt = this.datastore.prepareStatement( conn, query ); 423 LOG.logDebug( "Determining join table row: " + query ); 424 rs = stmt.executeQuery(); 425 if ( rs.next() ) { 426 Collection<KeyColumn> keyColumns = new ArrayList<KeyColumn>( selectFields.length ); 427 for ( int i = 0; i < selectFields.length; i++ ) { 428 KeyColumn column = new KeyColumn( selectFields[i].getField(), 429 selectFields[i].getType(), 430 rs.getObject( i + 1 ) ); 431 keyColumns.add( column ); 432 } 433 if ( subFid.getFeatureType().isAbstract() ) { 434 String localSubFtName = subFid.getFeatureType().getName().getLocalName(); 435 KeyColumn column = new KeyColumn( FT_COLUMN, Types.VARCHAR, localSubFtName ); 436 keyColumns.add( column ); 437 } 438 jtEntry = new TableNode( relation1.getToTable(), keyColumns ); 439 } else { 440 String msg = "This is impossible: No join table row between feature and subfeature!?"; 441 throw new DatastoreException( msg ); 442 } 443 } catch ( SQLException e ) { 444 LOG.logInfo( e.getMessage(), e ); 445 throw new DatastoreException( "Error in determineJTNode(): " + e.getMessage() ); 446 } finally { 447 try { 448 if ( rs != null ) { 449 try { 450 rs.close(); 451 } catch ( SQLException e ) { 452 LOG.logError( "Error closing result set: '" + e.getMessage() + "'.", e ); 453 } 454 } 455 } finally { 456 if ( stmt != null ) { 457 try { 458 stmt.close(); 459 } catch ( SQLException e ) { 460 LOG.logError( "Error closing statement: '" + e.getMessage() + "'.", e ); 461 } 462 } 463 } 464 } 465 return jtEntry; 466 } 467 468 /** 469 * Delete orphaned rows in the specified property table (target table of the given 470 * {@link TableRelation}). 471 * <p> 472 * Only used by the {@link UpdateHandler}. 473 * 474 * @param relation 475 * @param keyValues 476 * @throws DatastoreException 477 */ 478 public void deleteOrphanedPropertyRows( TableRelation relation, Object[] keyValues ) 479 throws DatastoreException { 480 Collection<KeyColumn> keyColumns = new ArrayList<KeyColumn>( keyValues.length ); 481 for ( int i = 0; i < keyValues.length; i++ ) { 482 KeyColumn keyColumn = new KeyColumn( relation.getToFields()[i].getField(), 483 relation.getToFields()[i].getType(), keyValues[i] ); 484 keyColumns.add( keyColumn ); 485 } 486 TableNode node = new TableNode( relation.getToTable(), keyColumns ); 487 if ( getReferencingRows( node ).size() == 0 ) { 488 performDelete( node ); 489 } 490 } 491 492 /** 493 * Returns all table rows that reference the given table row ({@link TableNode}). 494 * 495 * @param node 496 * @return all table rows that reference the given table row 497 * @throws DatastoreException 498 */ 499 private List<TableNode> getReferencingRows( TableNode node ) 500 throws DatastoreException { 501 502 List<TableNode> rows = new ArrayList<TableNode>(); 503 for ( TableReference tableReference : getReferencingTables( node.getTable() ) ) { 504 rows.addAll( getReferencingRows( node, tableReference ) ); 505 } 506 return rows; 507 } 508 509 /** 510 * Returns all stored rows (as {@link TableNode}s) that reference the given row ({@link TableNode}) 511 * via the also given reference relation. 512 * 513 * @param node 514 * @param ref 515 * @return all stored rows that reference the given row 516 * @throws DatastoreException 517 */ 518 private List<TableNode> getReferencingRows( TableNode node, TableReference ref ) 519 throws DatastoreException { 520 521 List<TableNode> referencingRows = new ArrayList<TableNode>(); 522 this.aliasGenerator.reset(); 523 String fromAlias = this.aliasGenerator.generateUniqueAlias(); 524 String toAlias = this.aliasGenerator.generateUniqueAlias(); 525 MappingField[] fromFields = ref.getFkColumns(); 526 MappingField[] toFields = ref.getKeyColumns(); 527 528 StatementBuffer query = new StatementBuffer(); 529 query.append( "SELECT DISTINCT " ); 530 for ( int i = 0; i < fromFields.length; i++ ) { 531 query.append( fromAlias ); 532 query.append( "." ); 533 query.append( fromFields[i].getField() ); 534 if ( i != fromFields.length - 1 ) { 535 query.append( ',' ); 536 } 537 } 538 query.append( " FROM " ); 539 query.append( ref.getFromTable() ); 540 query.append( " " ); 541 query.append( fromAlias ); 542 query.append( " INNER JOIN " ); 543 query.append( ref.getToTable() ); 544 query.append( " " ); 545 query.append( toAlias ); 546 query.append( " ON " ); 547 for ( int j = 0; j < fromFields.length; j++ ) { 548 query.append( fromAlias ); 549 query.append( '.' ); 550 query.append( fromFields[j].getField() ); 551 query.append( '=' ); 552 query.append( toAlias ); 553 query.append( '.' ); 554 query.append( toFields[j].getField() ); 555 } 556 query.append( " WHERE " ); 557 int i = node.getKeyColumns().size(); 558 for ( KeyColumn column : node.getKeyColumns() ) { 559 query.append( toAlias ); 560 query.append( '.' ); 561 query.append( column.getName() ); 562 query.append( "=?" ); 563 query.addArgument( column.getValue(), column.getTypeCode() ); 564 if ( --i != 0 ) { 565 query.append( " AND " ); 566 } 567 } 568 569 PreparedStatement stmt = null; 570 ResultSet rs = null; 571 try { 572 stmt = this.datastore.prepareStatement( conn, query ); 573 LOG.logDebug( "Performing: " + query ); 574 rs = stmt.executeQuery(); 575 while ( rs.next() ) { 576 Collection<KeyColumn> keyColumns = new ArrayList<KeyColumn>( fromFields.length ); 577 for ( i = 0; i < fromFields.length; i++ ) { 578 KeyColumn column = new KeyColumn( fromFields[i].getField(), 579 fromFields[i].getType(), rs.getObject( i + 1 ) ); 580 keyColumns.add( column ); 581 } 582 TableNode referencingRow = new TableNode( ref.getFromTable(), keyColumns ); 583 referencingRows.add( referencingRow ); 584 } 585 } catch ( SQLException e ) { 586 throw new DatastoreException( "Error in getReferencingRows(): " + e.getMessage() ); 587 } finally { 588 try { 589 if ( rs != null ) { 590 try { 591 rs.close(); 592 } catch ( SQLException e ) { 593 LOG.logError( "Error closing result set: '" + e.getMessage() + "'.", e ); 594 } 595 } 596 } finally { 597 if ( stmt != null ) { 598 try { 599 stmt.close(); 600 } catch ( SQLException e ) { 601 LOG.logError( "Error closing statement: '" + e.getMessage() + "'.", e ); 602 } 603 } 604 } 605 } 606 return referencingRows; 607 } 608 609 /** 610 * Returns all tables that reference the given table. 611 * 612 * TODO cache search 613 * 614 * @param table 615 * @return all tables that reference the given table 616 */ 617 private List<TableReference> getReferencingTables( String table ) { 618 619 List<TableReference> tables = new ArrayList<TableReference>(); 620 MappedGMLSchema[] schemas = this.datastore.getSchemas(); 621 for ( int i = 0; i < schemas.length; i++ ) { 622 MappedGMLSchema schema = schemas[i]; 623 FeatureType[] fts = schema.getFeatureTypes(); 624 for ( int j = 0; j < fts.length; j++ ) { 625 MappedFeatureType ft = (MappedFeatureType) fts[j]; 626 if ( !ft.isAbstract() ) { 627 PropertyType[] props = ft.getProperties(); 628 for ( int k = 0; k < props.length; k++ ) { 629 tables.addAll( getReferencingTables( (MappedPropertyType) props[k], table ) ); 630 } 631 } 632 } 633 } 634 return tables; 635 } 636 637 /** 638 * Returns all tables that reference the given table and that are defined in the mapping of the 639 * given property type. 640 * 641 * @param property 642 * @param table 643 * @return all tables that reference the given table 644 */ 645 private List<TableReference> getReferencingTables( MappedPropertyType property, String table ) { 646 647 List<TableReference> tables = new ArrayList<TableReference>(); 648 if ( property instanceof MappedFeaturePropertyType 649 && ( (MappedFeaturePropertyType) property ).getFeatureTypeReference().getFeatureType().isAbstract() ) { 650 TableRelation[] relations = property.getTableRelations(); 651 for ( int j = 0; j < relations.length - 1; j++ ) { 652 TableReference ref = new TableReference( relations[j] ); 653 if ( ref.getToTable().equals( table ) ) { 654 tables.add( ref ); 655 } 656 } 657 MappedFeaturePropertyType pt = (MappedFeaturePropertyType) property; 658 MappedFeatureType abstractFt = pt.getFeatureTypeReference().getFeatureType(); 659 MappedFeatureType[] substitutions = abstractFt.getConcreteSubstitutions(); 660 for ( MappedFeatureType concreteType : substitutions ) { 661 TableRelation finalStep = relations[relations.length - 1]; 662 TableReference ref = new TableReference( getTableRelation( finalStep, 663 concreteType.getTable() ) ); 664 if ( ref.getToTable().equals( table ) ) { 665 tables.add( ref ); 666 } 667 } 668 669 } else { 670 TableRelation[] relations = property.getTableRelations(); 671 for ( int j = 0; j < relations.length; j++ ) { 672 TableReference ref = new TableReference( relations[j] ); 673 if ( ref.getToTable().equals( table ) ) { 674 tables.add( ref ); 675 } 676 } 677 } 678 return tables; 679 } 680 681 private TableRelation getTableRelation( TableRelation toAbstractSubFt, String table ) { 682 MappingField[] toConcreteFields = new MappingField[toAbstractSubFt.getToFields().length]; 683 for ( int i = 0; i < toConcreteFields.length; i++ ) { 684 MappingField toAbstractField = toAbstractSubFt.getToFields()[i]; 685 toConcreteFields[i] = new MappingField( table, toAbstractField.getField(), 686 toAbstractField.getType() ); 687 } 688 TableRelation toConcreteSubFt = new TableRelation( toAbstractSubFt.getFromFields(), 689 toConcreteFields, 690 toAbstractSubFt.getFKInfo(), 691 toAbstractSubFt.getIdGenerator() ); 692 return toConcreteSubFt; 693 } 694 }