/*
* 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.relational.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.osgi.util.NLS;
import org.teiid.core.designer.HashCodeUtil;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.StringUtilities;
import org.teiid.designer.metamodels.relational.aspects.validation.RelationalStringNameValidator;
import org.teiid.designer.relational.Messages;
import org.teiid.designer.relational.RelationalPlugin;
/**
*
*
* @since 8.0
*/
public class RelationalTable extends RelationalReference {
public static final String KEY_CARDINALITY = "CARDINALITY"; //$NON-NLS-1$
public static final String KEY_MATERIALIZED = "MATERIALIZED"; //$NON-NLS-1$
public static final String KEY_MATERIALIZED_TABLE = "MATERIALIZEDTABLE"; //$NON-NLS-1$
public static final String KEY_SUPPORTS_UPDATE = "SUPPORTSUPDATE"; //$NON-NLS-1$
public static final String KEY_SYSTEM = "SYSTEM"; //$NON-NLS-1$
public static final int DEFAULT_CARDINALITY = -1;
public static final boolean DEFAULT_MATERIALIZED = false;
public static final String DEFAULT_MATERIALIZED_TABLE = null;
public static final boolean DEFAULT_SUPPORTS_UPDATE = true;
public static final boolean DEFAULT_SYSTEM = false;
public static final String DEFAULT_DATATYPE = "string"; //$NON-NLS-1$
private int cardinality = DEFAULT_CARDINALITY;
private boolean materialized = DEFAULT_MATERIALIZED;
private String materializedTableName;
private String materializedTableModelPath;
private boolean supportsUpdate = DEFAULT_SUPPORTS_UPDATE;
private boolean system = DEFAULT_SYSTEM;
private List<RelationalColumn> columns;
private RelationalPrimaryKey primaryKey;
private List<RelationalUniqueConstraint> uniqueConstraints;
private List<RelationalAccessPattern> accessPatterns;
private List<RelationalForeignKey> foreignKeys;
private List<RelationalIndex> indexes;
private String nativeQuery;
/**
* RelationalTable constructor
*/
public RelationalTable() {
super();
setType(TYPES.TABLE);
init();
}
/**
* RelationalTable constructor
* @param name the table name
*/
public RelationalTable( String name ) {
super(name);
setType(TYPES.TABLE);
init();
}
private void init() {
this.columns = new ArrayList<RelationalColumn>();
this.accessPatterns = new ArrayList<RelationalAccessPattern>();
this.foreignKeys = new ArrayList<RelationalForeignKey>();
this.indexes = new ArrayList<RelationalIndex>();
this.uniqueConstraints = new ArrayList<RelationalUniqueConstraint>();
setNameValidator(new RelationalStringNameValidator(true));
}
/**
* @return cardinality
*/
public int getCardinality() {
return cardinality;
}
/**
* @param cardinality Sets cardinality to the specified value.
*/
public void setCardinality( int cardinality ) {
if( this.cardinality != cardinality ) {
this.cardinality = cardinality;
handleInfoChanged();
}
}
/**
* @return materialized
*/
public boolean isMaterialized() {
return materialized;
}
/**
* @param materialized Sets materialized to the specified value.
*/
public void setMaterialized( boolean materialized ) {
if( this.materialized != materialized ) {
this.materialized = materialized;
handleInfoChanged();
}
}
/**
* @return materializedTable
*/
public String getMaterializedTable() {
return materializedTableName;
}
/**
* @param materializedTable Sets materializedTable to the specified value.
*/
public void setMaterializedTable( String materializedTableName ) {
if( this.materializedTableName == null || !this.materializedTableName.equalsIgnoreCase(materializedTableName) ) {
this.materializedTableName = materializedTableName;
handleInfoChanged();
}
}
/**
* @return materializedTable
*/
public String getMaterializedTableModelPath() {
return materializedTableModelPath;
}
/**
* @param materializedTable Sets materializedTable to the specified value.
*/
public void setMaterializedTableModelPath( String materializedTableModelPath ) {
if( this.materializedTableModelPath == null || !this.materializedTableModelPath.equalsIgnoreCase(materializedTableModelPath) ) {
this.materializedTableModelPath = materializedTableModelPath;
handleInfoChanged();
}
}
/**
* @return supportsUpdate
*/
public boolean getSupportsUpdate() {
return supportsUpdate;
}
/**
* @param supportsUpdate Sets supportsUpdate to the specified value.
*/
public void setSupportsUpdate( boolean supportsUpdate ) {
if( this.supportsUpdate != supportsUpdate ) {
this.supportsUpdate = supportsUpdate;
handleInfoChanged();
}
}
/**
* @return system
*/
public boolean isSystem() {
return system;
}
/**
* @param system Sets system to the specified value.
*/
public void setSystem( boolean system ) {
if( this.system != system ) {
this.system = system;
handleInfoChanged();
}
}
/**
* @return nativeQuery may be null
*/
public String getNativeQuery() {
return nativeQuery;
}
/**
* @param newQuery sets nativeQuery to the specified value. may be null
*/
public void setNativeQuery( String newQuery ) {
if( StringUtilities.areDifferent(this.nativeQuery, newQuery) ) {
this.nativeQuery = newQuery;
handleInfoChanged();
}
}
/**
* @return columns
*/
public List<RelationalColumn> getColumns() {
return columns;
}
/**
* Add a column to the table
* @param column the column to add
*/
public void addColumn(RelationalColumn column) {
if( this.columns.add(column) ) {
column.setParent(this);
handleInfoChanged();
}
}
/**
* Remove a column from the table
* @param column the column to remove
*/
public boolean removeColumn(RelationalColumn column) {
if( this.columns.remove(column) ) {
handleInfoChanged();
return true;
}
return false;
}
/**
* @return primaryKeys
*/
public RelationalPrimaryKey getPrimaryKey() {
return primaryKey;
}
/**
* Set the tables PK
* @param pk the pk
*/
public void setPrimaryKey(RelationalPrimaryKey pk) {
if( this.primaryKey != pk ) {
if( pk != null ) {
pk.setParent(this);
}
this.primaryKey = pk;
handleInfoChanged();
}
}
/**
* Retained for backward compatibility with
* RelationalTableEditorPanel / ViewTableEditorPanel
*
* @deprecated
* @return uniqueContraints
*/
public RelationalUniqueConstraint getUniqueContraint() {
if (getUniqueConstraints() == null || getUniqueConstraints().isEmpty())
return null;
return getUniqueConstraints().iterator().next();
}
/**
* Set the single and only unique constraint
*
* @deprecated
* Retained for backward compatibility with
* RelationalTableEditorPanel / ViewTableEditorPanel
*
* @param uc the uc
*/
public void setUniqueConstraint(RelationalUniqueConstraint uc) {
if (uniqueConstraints != null)
uniqueConstraints.clear();
addUniqueConstraint(uc);
}
/**
* @return uniqueContraints
*/
public Collection<RelationalUniqueConstraint> getUniqueConstraints() {
return uniqueConstraints;
}
/**
* Add a unique constraint
* @param constraint the constraint
*/
public void addUniqueConstraint(RelationalUniqueConstraint constraint) {
if( this.uniqueConstraints.add(constraint) ) {
constraint.setParent(this);
handleInfoChanged();
}
}
/**
* Remove a unique constraint
* @param constraint the constraint
* @return 'true' if removed, 'false' if not
*/
public boolean removeUniqueConstraint(RelationalUniqueConstraint constraint) {
if( this.uniqueConstraints.remove(constraint) ) {
handleInfoChanged();
return true;
}
return false;
}
/**
* @return accessPatterns
*/
public List<RelationalAccessPattern> getAccessPatterns() {
return accessPatterns;
}
/**
* Add an AccessPattern to the table
* @param ap the AccessPattern
*/
public void addAccessPattern(RelationalAccessPattern ap) {
if( this.accessPatterns.add(ap) ) {
ap.setParent(this);
handleInfoChanged();
}
}
/**
* Remove an AccessPattern from the table
* @param ap the AccessPattern
* @return 'true' if removed, 'false' if not
*/
public boolean removeAccessPattern(RelationalAccessPattern ap) {
if( this.accessPatterns.remove(ap) ) {
handleInfoChanged();
return true;
}
return false;
}
/**
* @return foreignKeys
*/
public List<RelationalForeignKey> getForeignKeys() {
return foreignKeys;
}
/**
* Add FK to the table
* @param fk the fk
*/
public void addForeignKey(RelationalForeignKey fk) {
if( this.foreignKeys.add(fk) ) {
fk.setParent(this);
handleInfoChanged();
}
}
/**
* Remove FK from the table
* @param fk the fk
* @return 'true' if removed, 'false' if not.
*/
public boolean removeForeignKey(RelationalForeignKey fk) {
if( this.foreignKeys.remove(fk) ) {
handleInfoChanged();
return true;
}
return false;
}
/**
* @return indexes
*/
public List<RelationalIndex> getIndexes() {
return indexes;
}
/**
* @param index the index
*/
public void addIndex(RelationalIndex index) {
if( this.indexes.add(index) ) {
// NOTE: indexes are children of a schema so set parent to table's parent
index.setParent(this.getParent());
handleInfoChanged();
}
}
/**
* @param index the index
* @return if index was removed
*/
public boolean removeIndex(RelationalIndex index) {
if( this.indexes.remove(index) ) {
handleInfoChanged();
return true;
}
return false;
}
/**
* Set the object properties
* @param props the properties
*/
public void setProperties(Properties props) {
for( Object key : props.keySet() ) {
String keyStr = (String)key;
String value = props.getProperty(keyStr);
if( value != null && value.length() == 0 ) {
continue;
}
if( keyStr.equalsIgnoreCase(KEY_NAME) ) {
setName(value);
} else if(keyStr.equalsIgnoreCase(KEY_NAME_IN_SOURCE) ) {
setNameInSource(value);
} else if(keyStr.equalsIgnoreCase(KEY_DESCRIPTION) ) {
setDescription(value);
} else if(keyStr.equalsIgnoreCase(KEY_CARDINALITY) ) {
setCardinality(Integer.parseInt(value));
} else if(keyStr.equalsIgnoreCase(KEY_MATERIALIZED) ) {
setMaterialized(Boolean.parseBoolean(value));
} else if(keyStr.equalsIgnoreCase(KEY_SUPPORTS_UPDATE) ) {
setSupportsUpdate(Boolean.parseBoolean(value));
} else if(keyStr.equalsIgnoreCase(KEY_SYSTEM) ) {
setSystem(Boolean.parseBoolean(value));
}
}
}
public RelationalColumn createColumn() {
return createColumn(DEFAULT_DATATYPE, RelationalColumn.DEFAULT_STRING_LENGTH);
}
public RelationalColumn createColumn(String datatype, int length) {
return createColumn("newColumn_" + (getColumns().size() + 1), datatype, length); //$NON-NLS-1$
}
public RelationalColumn createColumn(String name, String datatype, int length) {
RelationalColumn newColumn = new RelationalColumn(name);
newColumn.setDatatype(datatype);
newColumn.setLength(length);
addColumn(newColumn);
return newColumn;
}
public boolean canMoveColumnUp(RelationalColumn column) {
return getColumnIndex(column) > 0;
}
public boolean canMoveColumnDown(RelationalColumn column) {
return getColumnIndex(column) < getColumns().size()-1;
}
private int getColumnIndex(RelationalColumn column) {
int i=0;
for( RelationalColumn existingColumn : getColumns() ) {
if( existingColumn == column) {
return i;
}
i++;
}
// Shouldn't ever get here!
return -1;
}
public void moveColumnUp(RelationalColumn theColumn) {
int startIndex = getColumnIndex(theColumn);
if( startIndex > 0 ) {
// Make Copy of List & get columnInfo of startIndex-1
RelationalColumn[] existingColumns = getColumns().toArray(new RelationalColumn[0]);
RelationalColumn priorColumn = existingColumns[startIndex-1];
existingColumns[startIndex-1] = theColumn;
existingColumns[startIndex] = priorColumn;
List<RelationalColumn> newColumns = new ArrayList<RelationalColumn>(existingColumns.length);
for( RelationalColumn info : existingColumns) {
newColumns.add(info);
}
this.columns = newColumns;
}
}
public void moveColumnDown(RelationalColumn theColumn) {
int startIndex = getColumnIndex(theColumn);
if( startIndex < (getColumns().size()-1) ) {
// Make Copy of List & get columnInfo of startIndex+1
RelationalColumn[] existingColumns = getColumns().toArray(new RelationalColumn[0]);
RelationalColumn afterColumn = existingColumns[startIndex+1];
existingColumns[startIndex+1] = theColumn;
existingColumns[startIndex] = afterColumn;
List<RelationalColumn> newColumns = new ArrayList<RelationalColumn>(existingColumns.length);
for( RelationalColumn info : existingColumns) {
newColumns.add(info);
}
this.columns = newColumns;
}
}
@Override
public void handleInfoChanged() {
super.handleInfoChanged();
// Set extension properties here??
if( this.nativeQuery != null ) {
getExtensionProperties().put(NATIVE_QUERY, this.nativeQuery );
} else getExtensionProperties().remove(NATIVE_QUERY);
}
@Override
public void validate() {
// Walk through the properties for the table and set the status
super.validate();
if( getStatus().getSeverity() == IStatus.ERROR ) {
return;
}
if( this.isMaterialized() && this.materializedTableName == null ) {
setStatus(new Status(IStatus.WARNING, RelationalPlugin.PLUGIN_ID,
Messages.validate_error_materializedTableHasNoTableDefined ));
return;
}
if( this.getPrimaryKey() != null && !this.getPrimaryKey().getStatus().isOK()) {
setStatus(this.getPrimaryKey().getStatus());
return;
}
if (getUniqueConstraints() != null) {
for (RelationalUniqueConstraint uniqueConstraint : getUniqueConstraints()) {
if( uniqueConstraint != null && !uniqueConstraint.getStatus().isOK()) {
setStatus(uniqueConstraint.getStatus());
return;
}
}
}
for( RelationalForeignKey fk : this.getForeignKeys() ) {
if( !fk.getStatus().isOK()) {
setStatus(fk.getStatus());
return;
}
}
// Check Column Status values
for( RelationalColumn col : getColumns() ) {
if( col.getStatus().getSeverity() == IStatus.ERROR ) {
setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID, col.getStatus().getMessage() ));
return;
}
}
// Check Column Status values
for( RelationalColumn outerColumn : getColumns() ) {
for( RelationalColumn innerColumn : getColumns() ) {
if( outerColumn != innerColumn ) {
if( outerColumn.getName().equalsIgnoreCase(innerColumn.getName())) {
setStatus(new Status(IStatus.ERROR, RelationalPlugin.PLUGIN_ID,
NLS.bind(Messages.validate_error_duplicateColumnNamesInTable, getName())));
return;
}
}
}
}
if( this.getColumns().isEmpty() ) {
if( this.getParent() != null && this.getParent() instanceof RelationalProcedure ) {
setStatus(new Status(IStatus.WARNING, RelationalPlugin.PLUGIN_ID,
Messages.validate_warning_noColumnsDefinedForResultSet ));
return;
} else {
setStatus(new Status(IStatus.WARNING, RelationalPlugin.PLUGIN_ID,
Messages.validate_warning_noColumnsDefined ));
return;
}
}
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals( final Object object ) {
if (!super.equals(object)) {
return false;
}
if (this == object)
return true;
if (object == null)
return false;
if (getClass() != object.getClass())
return false;
final RelationalTable other = (RelationalTable)object;
// string properties
if (!CoreStringUtil.valuesAreEqual(getNativeQuery(), other.getNativeQuery())) {
return false;
}
if( !(getCardinality()==other.getCardinality()) ||
!(getSupportsUpdate()==other.getSupportsUpdate()) ||
!(isMaterialized()==other.isMaterialized()) ||
!(isSystem()==other.isSystem())) {
return false;
}
if (materializedTableName == null) {
if (other.materializedTableName != null)
return false;
} else if (!materializedTableName.equals(other.materializedTableName))
return false;
if (materializedTableModelPath == null) {
if (other.materializedTableModelPath != null)
return false;
} else if (!materializedTableModelPath.equals(other.materializedTableModelPath))
return false;
if (uniqueConstraints == null) {
if (other.uniqueConstraints != null)
return false;
} else if (!uniqueConstraints.equals(other.uniqueConstraints))
return false;
if (primaryKey == null) {
if (other.primaryKey != null)
return false;
} else if (!primaryKey.equals(other.primaryKey))
return false;
// Columns
List<RelationalColumn> thisColumns = getColumns();
List<RelationalColumn> thatColumns = other.getColumns();
if (thisColumns.size() != thatColumns.size()) {
return false;
}
if (!thisColumns.isEmpty() && !thisColumns.equals(thatColumns)) {
return false;
}
// ForeignKeys
List<RelationalForeignKey> thisFKs = getForeignKeys();
List<RelationalForeignKey> thatFKs = other.getForeignKeys();
if (thisFKs.size() != thatFKs.size()) {
return false;
}
if (thisFKs.size()==1) {
if(!thisFKs.get(0).equals(thatFKs.get(0))) {
return false;
}
} else if(thisFKs.size()>1){
ReferenceComparator comparator = new ReferenceComparator();
List<RelationalForeignKey> sortedThisFKs = new ArrayList<RelationalForeignKey>(getForeignKeys());
List<RelationalForeignKey> sortedThatFKs = new ArrayList<RelationalForeignKey>(other.getForeignKeys());
Collections.sort(sortedThisFKs,comparator);
Collections.sort(sortedThatFKs,comparator);
if (!sortedThisFKs.equals(sortedThatFKs)) {
return false;
}
}
// Indexes
List<RelationalIndex> thisIndexes = getIndexes();
List<RelationalIndex> thatIndexes = other.getIndexes();
if (thisIndexes.size() != thatIndexes.size()) {
return false;
}
if (!thisIndexes.isEmpty() && !thisIndexes.equals(thatIndexes)) {
return false;
}
// AccessPatterns
List<RelationalAccessPattern> thisAPs = getAccessPatterns();
List<RelationalAccessPattern> thatAPs = other.getAccessPatterns();
if (thisAPs.size() != thatAPs.size()) {
return false;
}
if (!thisAPs.isEmpty() && !thisAPs.equals(thatAPs)) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = super.hashCode();
// string properties
if (!CoreStringUtil.isEmpty(getNativeQuery())) {
result = HashCodeUtil.hashCode(result, getNativeQuery());
}
result = HashCodeUtil.hashCode(result, getCardinality());
result = HashCodeUtil.hashCode(result, getSupportsUpdate());
result = HashCodeUtil.hashCode(result, isMaterialized());
result = HashCodeUtil.hashCode(result, isSystem());
if(materializedTableName!=null) {
result = HashCodeUtil.hashCode(result, materializedTableName);
}
if(materializedTableModelPath!=null) {
result = HashCodeUtil.hashCode(result, materializedTableModelPath);
}
if(uniqueConstraints!=null) {
result = HashCodeUtil.hashCode(result, uniqueConstraints);
}
if(primaryKey!=null) {
result = HashCodeUtil.hashCode(result, primaryKey);
}
List<RelationalColumn> cols = getColumns();
for(RelationalColumn col: cols) {
result = HashCodeUtil.hashCode(result, col);
}
List<RelationalForeignKey> fks = getForeignKeys();
for(RelationalForeignKey fk: fks) {
result = HashCodeUtil.hashCode(result, fk);
}
List<RelationalIndex> indexes = getIndexes();
for(RelationalIndex index: indexes) {
result = HashCodeUtil.hashCode(result, index);
}
List<RelationalAccessPattern> aps = getAccessPatterns();
for(RelationalAccessPattern ap: aps) {
result = HashCodeUtil.hashCode(result, ap);
}
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName());
sb.append(" : name = ").append(getName()); //$NON-NLS-1$
if( !getColumns().isEmpty() ) {
sb.append("\n\t").append(getColumns().size()).append(" columns"); //$NON-NLS-1$ //$NON-NLS-2$
for( RelationalColumn col : getColumns() ) {
sb.append("\n\tcol = ").append(col); //$NON-NLS-1$
}
}
if( primaryKey != null ) {
sb.append("\n\t").append("PK = ").append(primaryKey); //$NON-NLS-1$ //$NON-NLS-2$
}
if( uniqueConstraints != null ) {
sb.append("\n\t").append("UC = ").append(uniqueConstraints); //$NON-NLS-1$ //$NON-NLS-2$
}
if( !getAccessPatterns().isEmpty() ) {
sb.append("\n\t").append(getAccessPatterns().size()).append(" access patterns"); //$NON-NLS-1$ //$NON-NLS-2$
for( RelationalAccessPattern ap : getAccessPatterns() ) {
sb.append("\n\tap = ").append(ap); //$NON-NLS-1$
}
}
return sb.toString();
}
}