blob: dfdba81f11693e19e290747fd44ab9bb4e7895d7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation
*
******************************************************************************/
package org.eclipse.persistence.tools.db;
import java.util.Iterator;
import org.eclipse.persistence.tools.db.model.ELColumn;
import org.eclipse.persistence.tools.db.model.ELColumnPair;
import org.eclipse.persistence.tools.db.model.ELReference;
import org.eclipse.persistence.tools.gen.db.Column;
import org.eclipse.persistence.tools.gen.db.ConnectionProfile;
import org.eclipse.persistence.tools.gen.db.ForeignKey;
import org.eclipse.persistence.tools.gen.db.Table;
import org.eclipse.persistence.tools.utility.iterable.FilteringIterable;
import org.eclipse.persistence.tools.utility.iterable.TransformationIterable;
import org.eclipse.persistence.tools.utility.iterator.TransformationIterator;
/**
* The concrete implementation of {@link ForeignKey}.
* <p>
* Provisional API: This interface is part of an interim API that is still under development and
* expected to change significantly before reaching stability. It is available at this early stage
* to solicit feedback from pioneering adopters on the understanding that any code that uses this
* API will almost certainly be broken (repeatedly) as the API evolves.<p>
*
* @version 2.6
*/
@SuppressWarnings("nls")
public class EclipseLinkForeignKey implements ForeignKey {
/** lazy-initialized - but it can be 'null' so we use a flag */
private String defaultAttributeName;
private boolean defaultAttributeNameCalculated = false;
private ELReference reference;
private EclipseLinkTable table;
public EclipseLinkForeignKey(EclipseLinkTable table, ELReference reference) {
super();
this.table = table;
this.reference = reference;
}
private String buildDefaultAttributeName() {
if ( ! this.referencesSingleColumnPrimaryKey()) {
return null;
}
ColumnPair columnPair = this.getColumnPair();
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;
}
return baseColName.substring(0, _index);
}
/**
* {@inheritDoc}
*/
@Override
public String getAttributeName() {
String defaultName = this.getDefaultAttributeName();
return (defaultName != null) ? defaultName : this.getNonDefaultAttributeName();
}
/**
* {@inheritDoc}
*/
@Override
public Iterable<Column> getBaseColumns() {
return new TransformationIterable<ELColumn, Column>(new Iterable<ELColumn>() {
@Override
public Iterator<ELColumn> iterator() {
return new TransformationIterator<ELColumnPair, ELColumn>(EclipseLinkForeignKey.this.reference.columnPairs()) {
@Override
protected ELColumn transform(ELColumnPair column) {
return column.getSourceColumn();
}
};
}
}) {
@Override
protected Column transform(ELColumn column) {
return new EclipseLinkColumn(EclipseLinkForeignKey.this.table, column);
}
};
}
/**
* {@inheritDoc}
*/
@Override
public Table getBaseTable() {
return this.table;
}
/**
* {@inheritDoc}
*/
@Override
public ColumnPair getColumnPair() {
if (getColumnPairsSize() > 0) {
return getColumnPairs().iterator().next();
}
throw new IllegalStateException();
}
/**
* {@inheritDoc}
*/
@Override
public Iterable<ColumnPair> getColumnPairs() {
return new TransformationIterable<ELColumnPair, ForeignKey.ColumnPair>(reference.columnPairs()) {
@Override
protected ColumnPair transform(ELColumnPair columnPair) {
return new EclipseLinkColumnPair(EclipseLinkForeignKey.this.table, columnPair);
}
};
}
/**
* {@inheritDoc}
*/
@Override
public int getColumnPairsSize() {
return this.reference.columnPairsSize();
}
/**
* {@inheritDoc}
*/
@Override
public ConnectionProfile getConnectionProfile() {
throw new UnsupportedOperationException("Not Supported!");
}
/**
* {@inheritDoc}
*/
@Override
public EclipseLinkDatabase getDatabase() {
return this.table.getDatabase();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized String getDefaultAttributeName() {
if ( ! this.defaultAttributeNameCalculated) {
this.defaultAttributeNameCalculated = true;
this.defaultAttributeName = this.buildDefaultAttributeName();
}
return this.defaultAttributeName;
}
/**
* {@inheritDoc}
*/
@Override
public String getIdentifier() {
return getName();
}
/**
* {@inheritDoc}
*/
@Override
public String getIdentifier(String defaultName) {
return getDatabase().convertNameToIdentifier(getName());
}
/**
* Examples:<ul>
* <li>Oracle etc.<ul><code>
* <li>ForeignKey(FOO_ID => ID) vs. "foo" => null
* <li>ForeignKey(FOO_ID => FOO_ID) vs. "foo" => "FOO_ID"
* <li>ForeignKey(FOO => ID) vs. "foo" => "FOO"
* <li>ForeignKey(Foo_ID => ID) vs. "foo" => "\"Foo_ID\""
* </code></ul>
* <li>PostgreSQL etc.<ul><code>
* <li>ForeignKey(foo_id => id) vs. "foo" => null
* <li>ForeignKey(foo_id => foo_id) vs. "foo" => "foo_id"
* <li>ForeignKey(foo => id) vs. "foo" => "foo"
* <li>ForeignKey(Foo_ID => ID) vs. "foo" => "\"Foo_ID\""
* </code></ul>
* <li>SQL Server etc.<ul><code>
* <li>ForeignKey(foo_ID => ID) vs. "foo" => null
* <li>ForeignKey(FOO_ID => FOO_ID) vs. "foo" => "FOO_ID"
* <li>ForeignKey(FOO => ID) vs. "foo" => "FOO"
* <li>ForeignKey(Foo_ID => ID) vs. "foo" => "Foo_ID"
* </code></ul>
* </ul>
*/
@Override
public String getJoinColumnAnnotationIdentifier(String attributeName) {
String baseColumnName = this.getColumnPair().getBaseColumn().getName();
String defaultBaseColumnName = attributeName + '_' + this.getReferencedTable().getPrimaryKeyColumn().getName();
return getDatabase().getIdentifier(baseColumnName, defaultBaseColumnName);
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return this.reference.getName();
}
/**
* If this is a simple (single-column) foreign key, use the name of the
* single base column to build a name. If this is a compound foreign key,
* Returns the 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?
private String getNonDefaultAttributeName() {
return (this.getColumnPairsSize() == 1) ?
this.getNonDefaultAttributeNameFromBaseColumn() :
this.getReferencedTable().getName();
}
/**
* The underscore check is helpful when the referenced column is <em>not</em> the
* primary key of the referenced table (i.e. it has only a <em>unique</em> constraint).
* <pre>
* ForeignKey(EMP.CUBICLE_ID => CUBICLE.ID) => "CUBICLE"
* ForeignKey(EMP.CUBICLEID => CUBICLE.ID) => "CUBICLE"
* ForeignKey(EMP.CUBICLE_PK => CUBICLE.ID) => "CUBICLE_PK"
* </pre>
*/
private String getNonDefaultAttributeNameFromBaseColumn() {
ColumnPair columnPair = this.getColumnPair();
String baseColName = columnPair.getBaseColumn().getName();
String refColName = columnPair.getReferencedColumn().getName();
int len = baseColName.length();
int refLen = refColName.length();
if ((len > refLen) && baseColName.endsWith(refColName)) {
len = len - refLen;
if ((len > 1) && baseColName.charAt(len - 1) == '_') {
len = len - 1;
}
}
return baseColName.substring(0, len);
}
/**
* {@inheritDoc}
*/
@Override
public Iterable<Column> getNonPrimaryKeyBaseColumns() {
return new FilteringIterable<Column>(getBaseColumns()) {
@Override
protected boolean accept(Column column) {
return !column.isPartOfPrimaryKey();
}
};
}
/**
* {@inheritDoc}
*/
@Override
public Iterable<Column> getReferencedColumns() {
return new TransformationIterable<ELColumn, Column>(new Iterable<ELColumn>() {
@Override
public Iterator<ELColumn> iterator() {
return new TransformationIterator<ELColumnPair, ELColumn>(EclipseLinkForeignKey.this.reference.columnPairs()) {
@Override
protected ELColumn transform(ELColumnPair column) {
return column.getTargetColumn();
}
};
}
}) {
@Override
protected Column transform(ELColumn column) {
return new EclipseLinkColumn(EclipseLinkForeignKey.this.table, column);
}
};
}
/**
* {@inheritDoc}
*/
@Override
public Table getReferencedTable() {
return new EclipseLinkTable(this.table.getSchema(), this.reference.getTargetTable());
}
/**
* {@inheritDoc}
*/
@Override
public boolean referencesSingleColumnPrimaryKey() {
if (this.getColumnPairsSize() != 1) {
return false;
}
if (this.getReferencedTable().getPrimaryKeyColumnsSize() != 1) {
return false;
}
return this.getColumnPair().getReferencedColumn() == this.getReferencedTable().getPrimaryKeyColumn();
}
}