/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.metamodels.relational.provider;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.core.association.AssociationDescriptor;
import org.teiid.designer.core.association.AssociationProvider;
import org.teiid.designer.metamodels.relational.BaseTable;
import org.teiid.designer.metamodels.relational.Column;
import org.teiid.designer.metamodels.relational.ForeignKey;
import org.teiid.designer.metamodels.relational.PrimaryKey;
import org.teiid.designer.metamodels.relational.RelationalEntity;
import org.teiid.designer.metamodels.relational.RelationalPackage;
import org.teiid.designer.metamodels.relational.RelationalPlugin;
import org.teiid.designer.metamodels.relational.Table;
/**
* ForeignKeyAssociationProvider
*
* @since 8.0
*/
public class ForeignKeyAssociationProvider implements AssociationProvider {
public static final Class[] VALID_CLASSES_TYPES = new Class[] {Table.class, Column.class, PrimaryKey.class, ForeignKey.class};
// ==================================================================================
// I N T E R F A C E M E T H O D S
// ==================================================================================
/* (non-Javadoc)
* @See org.teiid.designer.core.association.AssociationProvider#getNewAssociationDescriptors(java.util.List)
*/
@Override
public Collection getNewAssociationDescriptors( List eObjects ) {
// If the list of selected objects contains invalid entities then return
if (!containsValidObjects(eObjects, VALID_CLASSES_TYPES)) {
return Collections.EMPTY_LIST;
}
// If the list of selected objects contains a insufficient number
// of tables either explicitly or implicitly referenced then return
final List tables = ForeignKeyAssociationProvider.getTables(eObjects);
if (tables.size() != 2) {
return Collections.EMPTY_LIST;
}
// ---------------------------------------------------------------------------------------
// Create a ForeignKeyAssociationDescriptor for any situation that is considered ambiguous
// ---------------------------------------------------------------------------------------
final Table tableA = (Table)tables.get(0);
final Table tableB = (Table)tables.get(1);
final PrimaryKey pkA = ForeignKeyAssociationProvider.getPrimaryKey(eObjects, tableA);
final PrimaryKey pkB = ForeignKeyAssociationProvider.getPrimaryKey(eObjects, tableB);
final ForeignKey fkA = ForeignKeyAssociationProvider.getForeignKey(eObjects, tableA);
final ForeignKey fkB = ForeignKeyAssociationProvider.getForeignKey(eObjects, tableB);
final List columnsA = ForeignKeyAssociationProvider.getColumns(eObjects, tableA);
final List columnsB = ForeignKeyAssociationProvider.getColumns(eObjects, tableB);
ForeignKeyAssociationDescriptor tmpDescr = null;
List tmp = new ArrayList(11);
// Ignore selected columns when both a PK and FK are selected
boolean columnsSelected = (!columnsA.isEmpty() && !columnsB.isEmpty());
if ((fkA != null && pkB != null && columnsSelected) || (fkB != null && pkA != null && columnsSelected)) {
List modifiedList = new ArrayList(eObjects);
modifiedList.remove(columnsA);
modifiedList.remove(columnsB);
tmpDescr = new ForeignKeyAssociationDescriptor(modifiedList);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_status_5"); //$NON-NLS-1$
tmpDescr.updateStatus(IStatus.OK, -1, msg, null);
tmpDescr.setOverwritePkRef(false);
tmpDescr.setText(RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_text_5")); //$NON-NLS-1$
tmp.add(tmpDescr);
}
// Reset PK reference on selected FK
if ((pkA != null && fkB != null && fkB.getUniqueKey() != null && fkB.getUniqueKey() != pkA)
|| (pkB != null && fkA != null && fkA.getUniqueKey() != null && fkA.getUniqueKey() != pkB)) {
tmpDescr = new ForeignKeyAssociationDescriptor(eObjects);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_status_1"); //$NON-NLS-1$
tmpDescr.updateStatus(IStatus.OK, -1, msg, null);
tmpDescr.setOverwritePkRef(true);
tmpDescr.setText(RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_text_1")); //$NON-NLS-1$
tmp.add(tmpDescr);
}
// Ignore selected columns in table containing PK
if ((pkA != null && !columnsA.isEmpty()) || (pkB != null && !columnsB.isEmpty())) {
List modifiedList = new ArrayList(eObjects);
if (pkA != null && !columnsA.isEmpty()) {
modifiedList.removeAll(columnsA);
} else {
modifiedList.removeAll(columnsB);
}
tmpDescr = new ForeignKeyAssociationDescriptor(modifiedList);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_status_2"); //$NON-NLS-1$
tmpDescr.updateStatus(IStatus.OK, -1, msg, null);
tmpDescr.setOverwritePkRef(false);
tmpDescr.setText(RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_text_2")); //$NON-NLS-1$
tmp.add(tmpDescr);
}
// Ignore selected columns when is also FK selected
if ((fkA != null && !columnsA.isEmpty()) || (fkB != null && !columnsB.isEmpty())) {
List modifiedList = new ArrayList(eObjects);
if (fkA != null && !columnsA.isEmpty()) {
modifiedList.removeAll(columnsA);
} else {
modifiedList.removeAll(columnsB);
}
tmpDescr = new ForeignKeyAssociationDescriptor(modifiedList);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_status_3"); //$NON-NLS-1$
tmpDescr.updateStatus(IStatus.OK, -1, msg, null);
tmpDescr.setOverwritePkRef(false);
tmpDescr.setText(RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_text_3")); //$NON-NLS-1$
tmp.add(tmpDescr);
}
// Ignore selected FK when columns are also selected
if ((fkA != null && !columnsA.isEmpty()) || (fkB != null && !columnsB.isEmpty())) {
List modifiedList = new ArrayList(eObjects);
if (fkA != null && !columnsA.isEmpty()) {
modifiedList.remove(fkA);
} else {
modifiedList.remove(fkB);
}
tmpDescr = new ForeignKeyAssociationDescriptor(modifiedList);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_status_4"); //$NON-NLS-1$
tmpDescr.updateStatus(IStatus.OK, -1, msg, null);
tmpDescr.setOverwritePkRef(false);
tmpDescr.setText(RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Ambiguous_foreign_key_relationship_text_4")); //$NON-NLS-1$
tmp.add(tmpDescr);
}
// If any situations were found that would be considered ambiguous ...
final List result = new ArrayList(1);
if (!tmp.isEmpty()) {
// Create a ForeignKeyAssociationDescriptor composed of all the possible
// ways to create the association
ForeignKeyAssociationDescriptor descriptor = new ForeignKeyAssociationDescriptor(eObjects);
ForeignKeyAssociationDescriptor firstDescr = (ForeignKeyAssociationDescriptor)tmp.get(0);
final String msg = RelationalPlugin.Util.getString("ForeignKeyAssociationProvider.Default_foreign_key_relationship_constructor_1") + firstDescr.getText(); //$NON-NLS-1$
descriptor.updateStatus(IStatus.WARNING, -1, msg, null);
descriptor.setAmbiguous(true);
// Add all descriptors
for (Iterator iter = tmp.iterator(); iter.hasNext();) {
AssociationDescriptor ad = (AssociationDescriptor)iter.next();
descriptor.addDescriptor(ad);
}
result.add(descriptor);
}
// Else create the one ForeignKeyAssociationDescriptor that can create
// the association from the clearly defined list of selected objects
else {
ForeignKeyAssociationDescriptor descriptor = new ForeignKeyAssociationDescriptor(eObjects);
descriptor.setAmbiguous(false);
result.add(descriptor);
}
return result;
}
/**
* MyDefect : 13663 added logic to eliminate data access associations
*
* @param obj
* @return
* @since 4.3
*/
private static boolean isDataAccessAssociation( Object obj ) {
if (!(obj instanceof EObject)) {
return true;
}
EClass eClass = ((EObject)obj).eClass();
RelationalPackage relationalPackage = RelationalPackage.eINSTANCE;
if (eClass != relationalPackage.getBaseTable() && eClass != relationalPackage.getPrimaryKey()
&& eClass != relationalPackage.getForeignKey() && eClass != relationalPackage.getColumn()) {
return true;
}
return false;
}
// ==================================================================================
// P U B L I C M E T H O D S
// ==================================================================================
/**
* Return true if the list of objects are allowable RelationalEntity instances that represent valid EObjects to be used in the
* creation of a foreign key relationship.
*/
static boolean containsValidObjects( final List eObjects,
final Class[] validClasses ) {
CoreArgCheck.isNotNull(eObjects);
CoreArgCheck.isNotNull(validClasses);
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
// Return false if the object is null
if (obj == null) {
return false;
}
// MyDefect : 13663 added logic to eliminate data access associations
if (isDataAccessAssociation(obj)) {
return false;
}
// Return false if the object is not in the relational model
if (!(obj instanceof RelationalEntity)) {
return false;
}
// Return false if the object is not a valid class instance
boolean validClassInstance = false;
for (int i = 0; i < validClasses.length; i++) {
if (validClasses[i].isInstance(obj)) {
validClassInstance = true;
break;
}
}
if (!validClassInstance) {
return false;
}
}
return true;
}
/**
* Gather all Table instances either implicitly or explicitly defined in the list of objects. The list may contain instances
* of Table, Column, PrimaryKey, and ForeignKey.
*
* @return
*/
static List getTables( final List eObjects ) {
CoreArgCheck.isNotNull(eObjects);
final List result = new ArrayList(eObjects.size());
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
Table table = null;
if (obj instanceof Table) {
table = (Table)obj;
} else if (obj instanceof Column && ((Column)obj).eContainer() != null) {
Object container = ((Column)obj).eContainer();
if (container instanceof Table) {
table = (Table)container;
}
} else if (obj instanceof PrimaryKey && ((PrimaryKey)obj).eContainer() != null) {
table = (Table)((PrimaryKey)obj).eContainer();
} else if (obj instanceof ForeignKey && ((ForeignKey)obj).eContainer() != null) {
table = (Table)((ForeignKey)obj).eContainer();
}
if (table != null && table instanceof BaseTable && !result.contains(table)) {
result.add(table);
}
}
return result;
}
/**
* Gather all Column instances from the selection list of objects that are contained by the specified Table
*
* @return
*/
static List getColumns( final List eObjects,
final Table container ) {
CoreArgCheck.isNotNull(eObjects);
CoreArgCheck.isNotNull(container);
final List result = new ArrayList(eObjects.size());
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
if (obj instanceof Column && ((Column)obj).eContainer() == container) {
result.add(obj);
}
}
return result;
}
/**
* Gather all ForeignKey instances from the selection list of objects that are contained by the specified Table
*
* @return
*/
static List getForeignKeys( final List eObjects,
final Table container ) {
CoreArgCheck.isNotNull(eObjects);
CoreArgCheck.isNotNull(container);
final List result = new ArrayList(eObjects.size());
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
if (obj instanceof ForeignKey && ((ForeignKey)obj).eContainer() == container) {
result.add(obj);
}
}
return result;
}
/**
* Return the first ForeignKey instance from the selection list of objects that is contained by the specified Table
*
* @return
*/
static ForeignKey getForeignKey( final List eObjects,
final Table container ) {
CoreArgCheck.isNotNull(eObjects);
CoreArgCheck.isNotNull(container);
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
if (obj instanceof ForeignKey && ((ForeignKey)obj).eContainer() == container) {
return (ForeignKey)obj;
}
}
return null;
}
/**
* Return the PrimaryKey instance from the selection list of objects that is contained by the specified Table
*
* @return
*/
static PrimaryKey getPrimaryKey( final List eObjects,
final Table container ) {
CoreArgCheck.isNotNull(eObjects);
CoreArgCheck.isNotNull(container);
for (Iterator iter = eObjects.iterator(); iter.hasNext();) {
final Object obj = iter.next();
if (obj instanceof PrimaryKey && ((PrimaryKey)obj).eContainer() == container) {
return (PrimaryKey)obj;
}
}
return null;
}
static boolean isAmbiguous( final List eObjects ) {
final List tables = ForeignKeyAssociationProvider.getTables(eObjects);
final Table tableA = (Table)tables.get(0);
final Table tableB = (Table)tables.get(1);
final PrimaryKey pkA = ForeignKeyAssociationProvider.getPrimaryKey(eObjects, tableA);
final PrimaryKey pkB = ForeignKeyAssociationProvider.getPrimaryKey(eObjects, tableB);
final ForeignKey fkA = ForeignKeyAssociationProvider.getForeignKey(eObjects, tableA);
final ForeignKey fkB = ForeignKeyAssociationProvider.getForeignKey(eObjects, tableB);
final List columnsA = ForeignKeyAssociationProvider.getColumns(eObjects, tableA);
final List columnsB = ForeignKeyAssociationProvider.getColumns(eObjects, tableB);
if (pkA != null && fkB != null && fkB.getUniqueKey() != pkA) {
return true;
}
if (pkB != null && fkA != null && fkA.getUniqueKey() != pkB) {
return true;
}
if (pkA != null && !columnsA.isEmpty()) {
return true;
}
if (pkB != null && !columnsB.isEmpty()) {
return true;
}
return false;
}
}