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