blob: 653c88da5d3f9bfd24a04578055737b45a772b5d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2012 Oracle. 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.gen.internal;
import java.io.Serializable;
import java.util.List;
import org.eclipse.persistence.tools.gen.db.ForeignKey;
import org.eclipse.persistence.tools.utility.StringUtil;
/**
* Represents an ORM association. There are two types of associations:
* <ul>
* <li>simple association: An association between two database tables. The <em>referrer</em>
* table is the one containing the foreign key, the <em>referenced</em> table is the other
* party.
* </li>
* <li>many to many association: An association between two tables joined bya <em>join table</em>.
* In the example AUTHOR, BOOK, AUTHOR_BOOK, The referrer and referenced are AUTHOR and BOOK,
* and the join table is AUTHOR_BOOK.
* </li>
* </ul>
* <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.
*
* @version 2.5
*/
@SuppressWarnings("nls")
public class Association implements Serializable {
private final static long serialVersionUID = 2;
public static final String MANY_TO_ONE = "many-to-one";
public static final String MANY_TO_MANY = "many-to-many";
public static final String ONE_TO_ONE = "one-to-one";
public static final String ONE_TO_MANY = "one-to-many";
public static final String BI_DI = "bi-di";
public static final String NORMAL_DI = "normal-di"; //referrer->referenced
public static final String OPPOSITE_DI = "opposite-di"; //referenced->referrer
private transient ORMGenCustomizer mCustomizer;
private String mReferrerTableName;
private String mReferencedTableName;
private String mJoinTableName;
private List<String> mReferrerColNames;
private List<String> mReferencedColNames;
private List<String> mReferrerJoinColNames;
private List<String> mReferencedJoinColNames;
private transient List<ORMGenColumn> mReferrerCols;
private transient List<ORMGenColumn> mReferencedCols;
private transient List<ORMGenColumn> mReferrerJoinCols;
private transient List<ORMGenColumn> mReferencedJoinCols;
private String mCardinality;
private String mDirectionality;
private byte mFlags = GENERATED;
private AssociationRole mReferrerRole;
private AssociationRole mReferencedRole;
private transient ForeignKey mForeignKey;
/*constants for mFlags*/
/*whether the association should be generated*/
private static final byte GENERATED = 1 << 0;
/*whether the association is custom (i.e is not auto computed from foreign keys relationships).*/
private static final byte CUSTOM = 1 << 1;
/**
* The simple association constructor.
* The 2 tables are joined when the values of each column in
* referrerColNames match its corresponding column in referencedColNames.
*
* @param referrerTableName The "foreign key" table.
* @param referrerColNames The column names in the referrer table.
* @param referencedTableName The "primary key" table.
* @param referencedColNames The column names in the referenced table.
*/
public Association(ORMGenCustomizer customizer, String referrerTableName, List<String> referrerColNames
, String referencedTableName, List<String> referencedColNames) {
super();
mCustomizer = customizer;
mReferrerTableName = referrerTableName;
mReferencedTableName = referencedTableName;
mReferrerColNames = referrerColNames;
mReferencedColNames = referencedColNames;
mCardinality = MANY_TO_ONE;
mDirectionality = BI_DI;
setCustom(true);
}
/**
* The many to many constructor.
* The 2 tables are joined when the values of each column in
* referrerColNames match its corresponding column in referrerJoinColNames
* , and each column in referencedColNames match its corresponding column in referencedJoinColNames.
*
*/
public Association(ORMGenCustomizer customizer, String referrerTableName, List<String> referrerColNames
, String referencedTableName, List<String> referencedColNames
, String joinTableName, List<String> referrerJoinColNames, List<String> referencedJoinColNames) {
super();
mCustomizer = customizer;
mReferrerTableName = referrerTableName;
mReferencedTableName = referencedTableName;
mReferrerColNames = referrerColNames;
mReferencedColNames = referencedColNames;
mJoinTableName = joinTableName;
mReferrerJoinColNames = referrerJoinColNames;
mReferencedJoinColNames = referencedJoinColNames;
mCardinality = MANY_TO_MANY;
mDirectionality = BI_DI;
setCustom(true);
}
/**
* Empty constructor needed by the deserialization (should not be used otherwise).
*/
public Association() {
}
/**
* Computes the cardinality basedon the forign key definitions.
*/
public void computeCardinality() {
/*by default the association is many-to-one unless the foreign key
* is also the primary key, in which case it is a one-to-one.*/
mCardinality = MANY_TO_ONE;
List<ORMGenColumn> referrerCols = getReferrerColumns();
List<ORMGenColumn> pkCols = getReferrerTable().getPrimaryKeyColumns();
if (pkCols.size() == referrerCols.size()) {
boolean isFkPk = true;
for (int i = 0, n = pkCols.size(); i < n; ++i) {
if (!pkCols.get(i).getName().equals(referrerCols.get(i).getName())) {
isFkPk = false;
break;
}
}
if (isFkPk) {
mCardinality = ONE_TO_ONE;
}
}
setCustom(false);
}
/**
* Called after the asscociations are deserialized to attach
* the customizer object.
*/
protected void restore(ORMGenCustomizer customizer) {
mCustomizer = customizer;
if (mReferrerRole != null) {
mReferrerRole.restore(this);
}
if (mReferencedRole != null) {
mReferencedRole.restore(this);
}
}
public ORMGenTable getReferrerTable() {
return mCustomizer.getTable(mReferrerTableName);
}
public String getReferrerTableName() {
return mReferrerTableName;
}
public ORMGenTable getReferencedTable() {
return mCustomizer.getTable(mReferencedTableName);
}
public String getReferencedTableName() {
return mReferencedTableName;
}
public ORMGenTable getJoinTable() {
return mCustomizer.getTable(mJoinTableName);
}
public String getJoinTableName() {
return mJoinTableName;
}
/**
* Returns the <code>ORMGenColumn</code> objects for the referrer
* columns.
*/
public List<ORMGenColumn> getReferrerColumns() {
if (mReferrerCols == null) {
ORMGenTable referrerTable = getReferrerTable();
mReferrerCols = referrerTable.getColumnsByNames(mReferrerColNames);
}
return mReferrerCols;
}
public List<String> getReferrerColumnNames() {
return mReferrerColNames;
}
/**
* Returns the <code>ORMGenColumn</code> objects for the referenced
* columns.
*/
public List<ORMGenColumn> getReferencedColumns() {
if (mReferencedCols == null) {
mReferencedCols = getReferencedTable().getColumnsByNames(mReferencedColNames);
}
return mReferencedCols;
}
public List<String> getReferencedColumnNames() {
return mReferencedColNames;
}
public List<ORMGenColumn> getReferrerJoinColumns() {
if (mReferrerJoinCols == null) {
mReferrerJoinCols = getJoinTable().getColumnsByNames(mReferrerJoinColNames);
}
return mReferrerJoinCols;
}
public List<String> getReferrerJoinColumnNames() {
return mReferrerJoinColNames;
}
public List<ORMGenColumn> getReferencedJoinColumns() {
if (mReferencedJoinCols == null) {
mReferencedJoinCols = getJoinTable().getColumnsByNames(mReferencedJoinColNames);
}
return mReferencedJoinCols;
}
public List<String> getReferencedJoinColumnNames() {
return mReferencedJoinColNames;
}
/**
* Returns the association cardinality, one of {@link #MANY_TO_ONE}|{@link #MANY_TO_MANY}
* |{@link #ONE_TO_ONE}|{@link #ONE_TO_MANY}
*/
public String getCardinality() {
return mCardinality;
}
public void setCardinality(String cardinality) {
assert(cardinality.equals(MANY_TO_ONE) || cardinality.equals(MANY_TO_MANY) || cardinality.equals(ONE_TO_ONE) || cardinality.equals(ONE_TO_MANY));
mCardinality = cardinality;
}
/**
* Returns the association directionality, one of {@link #BI_DI}|{@link #NORMAL_DI}
* |{@link #OPPOSITE_DI}
*/
public String getDirectionality() {
return mDirectionality;
}
public void setDirectionality(String dir) {
assert(dir.equals(BI_DI) || dir.equals(NORMAL_DI) || dir.equals(OPPOSITE_DI));
if (!dir.equals(mDirectionality)) {
mDirectionality = dir;
if (dir.equals(NORMAL_DI)) {
mReferencedRole = null;
} else if (dir.equals(OPPOSITE_DI)) {
mReferrerRole = null;
}
}
}
/**
* Tests whether this association is bidirectional.
* This is a shortcut for <code>getDirectionality().equals(BI_DI)</code>.
*/
public boolean isBidirectional() {
return mDirectionality.equals(BI_DI);
}
/**
* Returns true of this association should be generated.
*/
public boolean isGenerated() {
return (mFlags & GENERATED) != 0;
}
public void setGenerated(boolean generated) {
if (generated != isGenerated()) {
if (generated) {
mFlags |= GENERATED;
} else {
mFlags &= ~GENERATED;
}
mReferrerRole = mReferencedRole = null;
}
}
/**
* Returns true of this association is custom (i.e is not auto computed from foreign keys relationships).
*/
public boolean isCustom() {
return (mFlags & CUSTOM) != 0;
}
public void setCustom(boolean custom) {
if (custom) {
mFlags |= CUSTOM;
} else {
mFlags &= ~CUSTOM;
}
}
/**
* Returns the association role for the referrer side, or null
* if none (i.e if the directionality does not include it).
*/
public AssociationRole getReferrerRole() {
if (mReferrerRole == null && isGenerated()) {
if (!getDirectionality().equals(OPPOSITE_DI)) { //BI_DI or NORMAL_DI
mReferrerRole = new AssociationRole(this, true/*isReferrerEnd*/);
}
}
return mReferrerRole;
}
/**
* Returns the association role for the referenced side, or null
* if none (i.e if the directionality does not include it).
*/
public AssociationRole getReferencedRole() {
if (mReferencedRole == null && isGenerated()) {
if (!getDirectionality().equals(Association.NORMAL_DI)) { //BI_DI or OPPOSITE_DI
mReferencedRole = new AssociationRole(this, false/*isReferrerEnd*/);
}
}
return mReferencedRole;
}
/**
* Tests whether this association is valid (valid table and column names).
*/
protected boolean isValid(){
if (!isValidTableAndColumns(mReferrerTableName, mReferrerColNames)
|| !isValidTableAndColumns(mReferencedTableName, mReferencedColNames)) {
return false;
}
if (mJoinTableName != null) {
if (!isValidTableAndColumns(mJoinTableName, mReferrerJoinColNames)
|| !isValidTableAndColumns(mJoinTableName, mReferencedJoinColNames)) {
return false;
}
}
return true;
}
private boolean isValidTableAndColumns(String tableName, List<String> columnNames) {
ORMGenTable table = mCustomizer.getTable(tableName);
if (table == null) {
return false;
}
for (int i = 0, n = columnNames.size(); i < n; ++i) {
String colName = columnNames.get(i);
if (table.getColumnByName(colName) == null) {
return false;
}
}
return true;
}
public void setForeignKey(ForeignKey foreignKey) {
this.mForeignKey = foreignKey;
}
public ForeignKey getForeignKey(){
return this.mForeignKey;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if( this == obj )
return true;
if( obj instanceof Association ){
Association association2 = (Association)obj;
if (!this.getReferrerTableName().equals(association2.getReferrerTableName())
|| !this.getReferencedTableName().equals(association2.getReferencedTableName())
|| !StringUtil.equalObjects(this.getJoinTableName(), association2.getJoinTableName())
|| !this.getReferrerColumnNames().equals(association2.getReferrerColumnNames())
|| !this.getReferencedColumnNames().equals(association2.getReferencedColumnNames())
) {
return false;
}
/*the 2 association have the same referrer, referenced and join table*/
//If MTO or OTM association
if (this.getJoinTableName() == null) {
return true;
}
if (this.getReferrerJoinColumnNames().equals(association2.getReferrerJoinColumnNames())
&& this.getReferencedJoinColumnNames().equals(association2.getReferencedJoinColumnNames())) {
return true;
}
}
return false;
}
@Override
public String toString(){
return mReferrerTableName + " " + mCardinality + " " + mReferencedTableName ;
}
}