| /******************************************************************************* |
| * 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(); |
| } |
| } |