| /******************************************************************************* |
| * Copyright (c) 2006, 2008 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.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.datatools.connectivity.sqm.core.rte.ICatalogObject; |
| import org.eclipse.jpt.db.Column; |
| import org.eclipse.jpt.db.ForeignKey; |
| import org.eclipse.jpt.utility.internal.CollectionTools; |
| import org.eclipse.jpt.utility.internal.StringTools; |
| import org.eclipse.jpt.utility.internal.iterators.ArrayIterator; |
| import org.eclipse.jpt.utility.internal.iterators.FilteringIterator; |
| import org.eclipse.jpt.utility.internal.iterators.TransformationIterator; |
| |
| /** |
| * Wrap a DTP ForeignKey |
| */ |
| final class DTPForeignKeyWrapper |
| extends DTPWrapper |
| implements ForeignKey |
| { |
| // backpointer to parent |
| private final DTPTableWrapper baseTable; |
| |
| // the wrapped DTP foreign key |
| private final org.eclipse.datatools.modelbase.sql.constraints.ForeignKey dtpForeignKey; |
| |
| // lazy-initialized |
| private DTPTableWrapper referencedTable; |
| |
| // lazy-initialized |
| private LocalColumnPair[] columnPairs; |
| |
| // lazy-initialized |
| private String defaultEntityFieldName; |
| private boolean defaultEntityFieldNameCalculated = false; |
| |
| |
| // ********** constructor ********** |
| |
| DTPForeignKeyWrapper(DTPTableWrapper baseTable, org.eclipse.datatools.modelbase.sql.constraints.ForeignKey dtpForeignKey) { |
| super(baseTable); |
| this.baseTable = baseTable; |
| this.dtpForeignKey = dtpForeignKey; |
| } |
| |
| |
| // ********** DTPWrapper implementation ********** |
| |
| @Override |
| ICatalogObject getCatalogObject() { |
| return (ICatalogObject) this.dtpForeignKey; |
| } |
| |
| @Override |
| synchronized void catalogObjectChanged(int eventType) { |
| // clear stuff so it will be rebuilt |
| this.dispose_(); |
| this.getConnectionProfile().foreignKeyChanged(this, eventType); |
| } |
| |
| |
| // ********** ForeignKey implementation ********** |
| |
| @Override |
| public String getName() { |
| return this.dtpForeignKey.getName(); |
| } |
| |
| public DTPTableWrapper getBaseTable() { |
| return this.baseTable; |
| } |
| |
| public synchronized DTPTableWrapper getReferencedTable() { |
| if (this.referencedTable == null) { |
| this.referencedTable = this.baseTable.table(this.dtpForeignKey.getUniqueConstraint().getBaseTable()); |
| } |
| return this.referencedTable; |
| } |
| |
| // ***** column pairs |
| |
| public Iterator<ColumnPair> columnPairs() { |
| return new ArrayIterator<ColumnPair>(this.columnPairs_()); |
| } |
| |
| public LocalColumnPair columnPair() { |
| LocalColumnPair[] pairs = this.columnPairs_(); |
| if (pairs.length != 1) { |
| throw new IllegalStateException("multiple column pairs: " + pairs.length); //$NON-NLS-1$ |
| } |
| return pairs[0]; |
| } |
| |
| private Iterator<LocalColumnPair> localColumnPairs() { |
| return new ArrayIterator<LocalColumnPair>(this.columnPairs_()); |
| } |
| |
| private synchronized LocalColumnPair[] columnPairs_() { |
| if (this.columnPairs == null) { |
| this.columnPairs = this.buildColumnPairs(); |
| } |
| return this.columnPairs; |
| } |
| |
| private LocalColumnPair[] 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() + //$NON-NLS-1$ |
| " - mismatched sizes: " + size + " vs. " + refColumns.size()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| LocalColumnPair[] result = new LocalColumnPair[baseColumns.size()]; |
| for (int i = baseColumns.size(); i-- > 0; ) { |
| DTPColumnWrapper baseColumn = this.baseTable.column(baseColumns.get(i)); |
| DTPColumnWrapper refColumn = this.baseTable.column(refColumns.get(i)); |
| result[i] = new LocalColumnPair(baseColumn, refColumn); |
| } |
| return result; |
| } |
| |
| // minimize scope of suppressed warnings |
| @SuppressWarnings("unchecked") |
| private List<org.eclipse.datatools.modelbase.sql.tables.Column> dtpBaseColumns() { |
| return this.dtpForeignKey.getMembers(); |
| } |
| |
| // minimize scope of suppressed warnings |
| @SuppressWarnings("unchecked") |
| private List<org.eclipse.datatools.modelbase.sql.tables.Column> dtpRefColumns() { |
| return this.dtpForeignKey.getUniqueConstraint().getMembers(); |
| } |
| |
| public int columnPairsSize() { |
| return this.columnPairs_().length; |
| } |
| |
| public Iterator<Column> baseColumns() { |
| return new TransformationIterator<LocalColumnPair, Column>(this.localColumnPairs()) { |
| @Override |
| protected Column transform(LocalColumnPair pair) { |
| return pair.baseColumn(); |
| } |
| }; |
| } |
| |
| boolean baseColumnsContains(Column column) { |
| return CollectionTools.contains(this.baseColumns(), column); |
| } |
| |
| public Iterator<Column> nonPrimaryKeyBaseColumns() { |
| return new FilteringIterator<Column, Column>(this.baseColumns()) { |
| @Override |
| protected boolean accept(Column column) { |
| return ! DTPForeignKeyWrapper.this.getBaseTable().primaryKeyColumnsContains(column); |
| } |
| }; |
| } |
| |
| public Iterator<Column> referencedColumns() { |
| return new TransformationIterator<LocalColumnPair, Column>(this.localColumnPairs()) { |
| @Override |
| protected Column transform(LocalColumnPair columnPair) { |
| return columnPair.referencedColumn(); |
| } |
| }; |
| } |
| |
| // ***** 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 synchronized String defaultEntityFieldName() { |
| if ( ! this.defaultEntityFieldNameCalculated) { |
| this.defaultEntityFieldNameCalculated = true; |
| this.defaultEntityFieldName = this.buildDefaultEntityFieldName(); |
| } |
| return this.defaultEntityFieldName; |
| } |
| |
| /** |
| * @see #defaultEntityFieldName() |
| */ |
| private String buildDefaultEntityFieldName() { |
| if ( ! this.referencesSingleColumnPrimaryKey()) { |
| return null; |
| } |
| LocalColumnPair columnPair = this.columnPairs_()[0]; |
| String baseColName = columnPair.baseColumn().getName(); |
| String refColName = columnPair.referencedColumn().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(); |
| } |
| |
| // ***** miscellaneous |
| |
| public String getJavaFieldName() { |
| String fieldName = this.defaultEntityFieldName(); |
| return (fieldName != null) ? fieldName : this.nonDefaultEntityFieldName(); |
| } |
| |
| public boolean defaultMatchesJavaFieldName(String javaFieldName) { |
| return this.isCaseSensitive() ? |
| javaFieldName.equals(this.defaultEntityFieldName()) |
| : |
| javaFieldName.equalsIgnoreCase(this.defaultEntityFieldName()); |
| } |
| |
| public boolean isDefaultFor(String javaFieldName) { |
| if (this.columnPairsSize() != 1) { |
| return false; |
| } |
| |
| if (this.getReferencedTable().primaryKeyColumnsSize() != 1) { |
| return false; |
| } |
| |
| LocalColumnPair columnPair = this.columnPairs_()[0]; |
| Column pkColumn = this.getReferencedTable().primaryKeyColumn(); |
| if (columnPair.referencedColumn() != pkColumn) { |
| return false; |
| } |
| |
| return columnPair.baseColumn().matchesJavaFieldName(javaFieldName + "_" + pkColumn.getName()); //$NON-NLS-1$ |
| } |
| |
| public boolean referencesSingleColumnPrimaryKey() { |
| if (this.columnPairsSize() != 1) { |
| return false; |
| } |
| if (this.getReferencedTable().primaryKeyColumnsSize() != 1) { |
| return false; |
| } |
| |
| return this.columnPair().referencedColumn() == this.getReferencedTable().primaryKeyColumn(); |
| } |
| |
| /** |
| * 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.columnPair().baseColumn().getJavaFieldName() |
| : |
| this.getReferencedTable().getJavaFieldName(); |
| } |
| |
| |
| // ********** Comparable implementation ********** |
| |
| public int compareTo(ForeignKey foreignKey) { |
| return Collator.getInstance().compare(this.getName(), foreignKey.getName()); |
| } |
| |
| |
| // ********** internal methods ********** |
| |
| boolean wraps(org.eclipse.datatools.modelbase.sql.constraints.ForeignKey foreignKey) { |
| return this.dtpForeignKey == foreignKey; |
| } |
| |
| boolean isCaseSensitive() { |
| return this.baseTable.isCaseSensitive(); |
| } |
| |
| DTPDatabaseWrapper database() { |
| return this.baseTable.database(); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.getName() + ": " + Arrays.asList(this.columnPairs_())); //$NON-NLS-1$ |
| } |
| |
| |
| // ********** disposal ********** |
| |
| @Override |
| synchronized void dispose() { |
| this.dispose_(); |
| super.dispose(); |
| } |
| |
| private void dispose_() { |
| // the foreign key does not "contain" any other objects, |
| // so we don't need to forward the #dispose() |
| this.defaultEntityFieldNameCalculated = false; |
| this.defaultEntityFieldName = null; |
| this.columnPairs = null; |
| this.referencedTable = null; |
| } |
| |
| |
| // ********** column pair implementation ********** |
| |
| private static class LocalColumnPair implements ColumnPair { |
| private final DTPColumnWrapper baseColumn; |
| private final DTPColumnWrapper referencedColumn; |
| |
| LocalColumnPair(DTPColumnWrapper baseColumn, DTPColumnWrapper referencedColumn) { |
| super(); |
| this.baseColumn = baseColumn; |
| this.referencedColumn = referencedColumn; |
| } |
| |
| |
| // ********** ColumnPair implementation ********** |
| |
| public DTPColumnWrapper baseColumn() { |
| return this.baseColumn; |
| } |
| |
| public DTPColumnWrapper referencedColumn() { |
| return this.referencedColumn; |
| } |
| |
| |
| // ********** Comparable implementation ********** |
| |
| public int compareTo(ColumnPair columnPair) { |
| return Collator.getInstance().compare(this.baseColumn().getName(), columnPair.baseColumn().getName()); |
| } |
| |
| |
| // ********** Object overrides ********** |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.baseColumn.getName() + "=>" + this.referencedColumn.getName()); //$NON-NLS-1$ |
| } |
| |
| } |
| |
| } |