blob: 628771197870f6b13d0d23fe045641dcbce71126 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2007 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.db.internal;
import java.text.Collator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.datatools.connectivity.sqm.core.rte.ICatalogObject;
import org.eclipse.datatools.connectivity.sqm.core.rte.ICatalogObjectListener;
import org.eclipse.jpt.utility.internal.StringTools;
import org.eclipse.jpt.utility.internal.iterators.FilteringIterator;
import org.eclipse.jpt.utility.internal.iterators.TransformationIterator;
/**
* Wrap a DTP ForeignKey
*/
public final class ForeignKey extends DTPWrapper implements Comparable<ForeignKey> {
private final Table baseTable;
private final org.eclipse.datatools.modelbase.sql.constraints.ForeignKey dtpForeignKey;
private ICatalogObjectListener foreignKeyListener;
private Table referencedTable; // lazy-initialized
private Set<ColumnPair> columnPairs; // lazy-initialized
private String defaultEntityFieldName; // lazy-initialized
private boolean defaultEntityFieldNameCalculated = false;
// ********** constructors **********
ForeignKey(Table baseTable, org.eclipse.datatools.modelbase.sql.constraints.ForeignKey dtpForeignKey) {
super();
this.baseTable = baseTable;
this.dtpForeignKey = dtpForeignKey;
this.initialize();
}
// ********** behavior **********
private void initialize() {
if( this.connectionIsOnline()) {
this.foreignKeyListener = this.buildForeignKeyListener();
this.addCatalogObjectListener(( ICatalogObject) this.dtpForeignKey, this.foreignKeyListener);
}
}
@Override
protected boolean connectionIsOnline() {
return this.baseTable.connectionIsOnline();
}
private ICatalogObjectListener buildForeignKeyListener() {
return new ICatalogObjectListener() {
public void notifyChanged( final ICatalogObject foreignKey, final int eventType) {
// TODO
// if( foreignKey == ForeignKey.this.dtpForeignKey) {
// ForeignKey.this.baseTable.foreignKeyChanged( ForeignKey.this, eventType);
// }
}
};
}
@Override
protected void dispose() {
this.removeCatalogObjectListener(( ICatalogObject) this.dtpForeignKey, this.foreignKeyListener);
}
// ********** queries **********
public Table getBaseTable() {
return this.baseTable;
}
@Override
public String getName() {
return this.dtpForeignKey.getName();
}
boolean isCaseSensitive() {
return this.baseTable.isCaseSensitive();
}
boolean wraps(org.eclipse.datatools.modelbase.sql.constraints.ForeignKey foreignKey) {
return this.dtpForeignKey == foreignKey;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.getName() + ": " + this.getColumnPairs());
}
public Table getReferencedTable() {
if (this.referencedTable == null) {
this.referencedTable = this.baseTable.table(this.dtpForeignKey.getUniqueConstraint().getBaseTable());
}
return this.referencedTable;
}
/**
* return the foreign key's "base" columns
*/
public Iterator<Column> baseColumns() {
return new TransformationIterator<ColumnPair, Column>(this.columnPairs()) {
@Override
protected Column transform(ColumnPair pair) {
return pair.getBaseColumn();
}
};
}
/**
* return the foreign key's "base" columns that are not part of
* the base table's primary key
*/
public Iterator<Column> nonPrimaryKeyBaseColumns() {
return new FilteringIterator<Column>(this.baseColumns()) {
@Override
protected boolean accept(Object o) {
return ! ForeignKey.this.getBaseTable().primaryKeyColumnsContains((Column) o);
}
};
}
/**
* return the foreign key's "referenced" columns
*/
public Iterator<Column> referencedColumns() {
return new TransformationIterator<ColumnPair, Column>(this.columnPairs()) {
@Override
protected Column transform(ColumnPair columnPair) {
return columnPair.getReferencedColumn();
}
};
}
public String javaFieldName() {
String fieldName = this.getDefaultEntityFieldName();
return (fieldName == null) ?
this.nonDefaultEntityFieldName()
:
fieldName;
}
public boolean matchesJavaFieldName(String javaFieldName) {
return this.isCaseSensitive() ?
javaFieldName.equals(this.getDefaultEntityFieldName())
:
javaFieldName.equalsIgnoreCase(this.getDefaultEntityFieldName());
}
public boolean isDefaultFor(String javaFieldName) {
if (this.columnPairsSize() != 1) {
return false;
}
if (this.getReferencedTable().primaryKeyColumnsSize() != 1) {
return false;
}
ColumnPair columnPair = this.columnPairs().next();
Column pkColumn = this.getReferencedTable().primaryKeyColumns().next();
if (columnPair.getReferencedColumn() != pkColumn) {
return false;
}
return columnPair.getBaseColumn().matchesJavaFieldName(javaFieldName + "_" + pkColumn.getName());
}
// ***** column pairs
private synchronized Set<ColumnPair> getColumnPairs() {
if (this.columnPairs == null) {
this.columnPairs = this.buildColumnPairs();
}
return this.columnPairs;
}
@SuppressWarnings("unchecked")
private List<org.eclipse.datatools.modelbase.sql.tables.Column> dtpBaseColumns() {
return this.dtpForeignKey.getMembers();
}
@SuppressWarnings("unchecked")
private List<org.eclipse.datatools.modelbase.sql.tables.Column> dtpRefColumns() {
return this.dtpForeignKey.getUniqueConstraint().getMembers();
}
private Set<ColumnPair> buildColumnPairs() {
List<org.eclipse.datatools.modelbase.sql.tables.Column> baseColumns = this.dtpBaseColumns();
int size = baseColumns.size();
List<org.eclipse.datatools.modelbase.sql.tables.Column> refColumns = this.dtpRefColumns();
if (refColumns.size() != size) {
throw new IllegalStateException(this.getBaseTable().getName() + "." + this.getName() +
" - mismatched sizes: " + size + " vs. " + refColumns.size());
}
Set<ColumnPair> result = new HashSet<ColumnPair>(baseColumns.size());
for (int i = baseColumns.size(); i-- > 0; ) {
Column baseColumn = this.baseTable.column(baseColumns.get(i));
Column refColumn = this.baseTable.column(refColumns.get(i));
result.add(new ColumnPair(baseColumn, refColumn));
}
return result;
}
public Iterator<ColumnPair> columnPairs() {
return this.getColumnPairs().iterator();
}
public int columnPairsSize() {
return this.getColumnPairs().size();
}
// ***** default entity field name
/**
* If the name of the "base" column adheres to the EJB standard for a
* default mapping (i.e. it ends with an underscore followed by the name
* of the "referenced" column, and the "referenced" column is the single
* primary key column of the "referenced" table), return the corresponding
* default entity field name:
* ForeignKey(EMP.CUBICLE_ID => CUBICLE.ID) => "CUBICLE"
* Return a null if it does not adhere to the EJB standard:
* ForeignKey(EMP.CUBICLE_ID => CUBICLE.CUBICLE_ID) => null
*/
private String getDefaultEntityFieldName() {
if ( ! this.defaultEntityFieldNameCalculated) {
this.defaultEntityFieldNameCalculated = true;
this.defaultEntityFieldName = this.buildDefaultEntityFieldName();
}
return this.defaultEntityFieldName;
}
/**
* @see #getDefaultEntityFieldName()
*/
private String buildDefaultEntityFieldName() {
if ( ! this.referencesSingleColumnPrimaryKey()) {
return null;
}
ColumnPair columnPair = this.columnPairs().next();
String baseColName = columnPair.getBaseColumn().getName();
String refColName = columnPair.getReferencedColumn().getName();
if (baseColName.length() <= (refColName.length() + 1)) {
return null;
}
if ( ! baseColName.endsWith(refColName)) {
return null;
}
int _index = baseColName.length() - refColName.length() - 1;
if (baseColName.charAt(_index) != '_') {
return null;
}
String name = baseColName.substring(0, _index);
return this.isCaseSensitive() ? name : name.toLowerCase();
}
/**
* Return whether the foreign key references the primary key of the
* "referenced" table and that primary key has only a single column.
*/
public boolean referencesSingleColumnPrimaryKey() {
if (this.columnPairsSize() != 1) {
return false;
}
if (this.getReferencedTable().primaryKeyColumnsSize() != 1) {
return false;
}
ColumnPair columnPair = this.columnPairs().next();
return columnPair.getReferencedColumn() == this.getReferencedTable().primaryKeyColumns().next();
}
/**
* If this is a simple (single-column) foreign key, return the java field
* name of the single base column. If this is a compound foreign key,
* return the java field name of the referenced table.
*/
// TODO if there is only one FK to a given table, use the table's name instead of the column's name?
// TODO if the FK column name ends with the PK column name, strip the PK column name?
private String nonDefaultEntityFieldName() {
return (this.columnPairsSize() == 1) ?
this.columnPairs().next().getBaseColumn().javaFieldName()
:
this.getReferencedTable().javaFieldName();
}
// ********** Comparable implementation **********
public int compareTo(ForeignKey foreignKey) {
return Collator.getInstance().compare(this.getName(), foreignKey.getName());
}
// ********** member class **********
public static class ColumnPair implements Comparable<ColumnPair> {
private final Column baseColumn;
private final Column referencedColumn;
ColumnPair(Column baseColumn, Column referencedColumn) {
super();
this.baseColumn = baseColumn;
this.referencedColumn = referencedColumn;
}
public Column getBaseColumn() {
return this.baseColumn;
}
public Column getReferencedColumn() {
return this.referencedColumn;
}
@Override
public String toString() {
return StringTools.buildToStringFor(this, baseColumn.getName() + "=>" + this.referencedColumn.getName());
}
public int compareTo(ColumnPair cp) {
return Collator.getInstance().compare(this.getBaseColumn().getName(), cp.getBaseColumn().getName());
}
}
}