/*
* 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.transformation.ui.reconciler;
import javax.lang.model.type.NullType;
import org.eclipse.emf.ecore.EObject;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.types.DatatypeManager;
import org.teiid.designer.query.IQueryFactory;
import org.teiid.designer.query.IQueryService;
import org.teiid.designer.query.sql.lang.IExpression;
import org.teiid.designer.query.sql.symbol.IAliasSymbol;
import org.teiid.designer.query.sql.symbol.IElementSymbol;
import org.teiid.designer.query.sql.symbol.IExpressionSymbol;
import org.teiid.designer.transformation.ui.PluginConstants;
import org.teiid.designer.transformation.ui.UiConstants;
import org.teiid.designer.transformation.util.RuntimeTypeConverter;
import org.teiid.designer.transformation.util.TransformationHelper;
import org.teiid.designer.transformation.util.TransformationMappingHelper;
import org.teiid.designer.transformation.util.TransformationSqlHelper;
import org.teiid.designer.type.IDataTypeManagerService;
/**
* Binding Business Object A Binding has the following properties: (1) Virtual Attribute - can be MetaObject or String(if creating
* new) (2) SQL Symbol
*
* @author Mark Drilling
*
* @since 8.0
*/
public class Binding {
private static final String SPACE = " "; //$NON-NLS-1$
private static final String AS = "AS"; //$NON-NLS-1$
private static final String FROM = "from"; //$NON-NLS-1$
private static final String TO = "to"; //$NON-NLS-1$
private static final String CR = "\n"; //$NON-NLS-1$
private static final String ATTRIBUTE_TYPE_NULL_TEXT = UiConstants.Util.getString("Binding.attributeTypeNull.text"); //$NON-NLS-1$
private static final String BINDING_SYMBOL_NULL_TEXT = UiConstants.Util.getString("Binding.bindingSymbolNull.text"); //$NON-NLS-1$
private static final String USE_SQL_SYMBOL_TEXT = UiConstants.Util.getString("Binding.useSqlSymbol.text"); //$NON-NLS-1$
private static final String USE_SQL_EXPRESSION_TEXT = UiConstants.Util.getString("Binding.useSqlExpression.text"); //$NON-NLS-1$
private static final String CONVERT_SQL_TEXT = UiConstants.Util.getString("Binding.convertSqlSymbol.text"); //$NON-NLS-1$
private static final String LOSS_OF_PRECISION_WARNING = UiConstants.Util.getString("Binding.lossOfPrecisionWarning.text"); //$NON-NLS-1$
private static final String RUNTIME_TYPES_EQUAL_TEXT = UiConstants.Util.getString("Binding.runtimeTypesEqual.text"); //$NON-NLS-1$
private static final String NO_CONVERSION_REQD_TEXT = UiConstants.Util.getString("Binding.noConversionReqd.text"); //$NON-NLS-1$
private static final String NO_CONVERSION_AVAIL_TEXT = UiConstants.Util.getString("Binding.noConversionAvail.text"); //$NON-NLS-1$
private static final String CANNOT_CONVERT_AGGREGATE_TEXT = UiConstants.Util.getString("Binding.cannotConvertAggregate.text"); //$NON-NLS-1$
// Binding attribute can be EObject or String (when creating new)
private Object attribute;
// Binding symbol is SingleElementSymbol
// private SingleElementSymbol sqlSymbol;
private EObject originalAttrDatatype;
private EObject newAttrDatatype;
private String newAttrName;
private IExpression originalSymbol;
private IExpression newSymbol;
private IExpression availableSymbolConversion;
// private boolean canConvertSql = false;
private boolean sqlWasConverted = false;
private boolean isInputParamBinding = false;
private String sqlConversionText;
/**
* Create a Binding given only the attribute
*
* @param attribute the target attribute
*/
public Binding( Object attribute ) {
setAttribute(attribute);
}
/**
* Create a Binding given both the attribute and the bound symbol
*
* @param attribute the target attribute
* @param symbol the symbol to be bound to the attribute
*/
public Binding( Object attribute,
IExpression symbol ) {
setAttribute(attribute);
setOriginalSymbol(symbol);
}
/**
* @return true if bound, false otherwise
*/
public boolean isBound() {
return (originalSymbol != null) ? true : false;
}
public void setInputParamBinding( boolean isInputParam ) {
this.isInputParamBinding = isInputParam;
}
public boolean isInputParamBinding() {
return this.isInputParamBinding;
}
/**
* @return true if there is a type conflict, false otherwise
*/
public boolean hasTypeConflict() {
boolean hasConflict = false;
EObject currentAttrDatatype = getCurrentAttrDatatype();
if (isBound()) {
// If the current attribute type is null, isConflict
if (currentAttrDatatype == null) {
hasConflict = true;
// attribute type not null, check
} else {
hasConflict = !TransformationMappingHelper.typesMatch(getCurrentSymbol(), currentAttrDatatype);
}
}
return hasConflict;
}
/**
* @return binding attribute
*/
public Object getAttribute() {
return attribute;
}
/**
* @return SQL Symbol binding
*/
public IExpression getOriginalSymbol() {
return originalSymbol;
}
/**
* Get the current SingleElementSymbol. If the symbol has been modified, the modified symbol is returned. If not, the original
* symbol is returned;
*
* @return SQL Symbol binding
*/
public IExpression getCurrentSymbol() {
IExpression result = originalSymbol;
if (newSymbol != null) {
result = newSymbol;
}
return result;
}
/**
* Get the current symbol's runtime type.
*
* @return the current symbols runtime type
*/
public String getCurrentSymbolRuntimeType() {
String runtimeType = null;
IExpression currentSymbol = getCurrentSymbol();
if (currentSymbol != null) {
runtimeType = RuntimeTypeConverter.getRuntimeType(currentSymbol);
}
return runtimeType;
}
/**
* Get the current symbol's length
*
* @return the current symbols length
*/
public int getCurrentSymbolLength() {
int length = 0;
IExpression currentSymbol = getCurrentSymbol();
if (currentSymbol != null) {
length = TransformationSqlHelper.getElementSymbolLength(currentSymbol);
}
return length;
}
/**
* Set the attribute
*
* @param attribute
*/
public void setAttribute( Object attribute ) {
this.attribute = attribute;
if (attribute != null && TransformationHelper.isSqlColumn(attribute)) {
originalAttrDatatype = TransformationHelper.getSqlColumnDatatype((EObject)attribute);
}
}
/**
* Set the SQL Symbol
*
* @param the SQL Symbol
*/
public void setOriginalSymbol( IExpression seSymbol ) {
this.originalSymbol = seSymbol;
this.newSymbol = null;
// Update SQL Conversion Data - based on symbol and attr type
if (seSymbol == null) {
availableSymbolConversion = null;
sqlConversionText = null;
// canConvertSql=false;
sqlWasConverted = false;
} else {
// If the attribute is "String", means this is new attribute. Takes on symbol type.
if (getAttribute() instanceof String) {
String symbolRuntimeType = RuntimeTypeConverter.getRuntimeType(seSymbol);
EObject datatype = TransformationMappingHelper.getDefaultDatatypeForRuntimeTypeName(symbolRuntimeType);
setNewAttrDatatype(datatype);
} else {
updateSqlConversionData();
}
}
}
/**
* Set the New SQL Symbol - this happens on bind
*
* @param the SQL Symbol
*/
public void setNewSymbol( IExpression seSymbol ) {
if (this.originalSymbol == null) {
this.originalSymbol = seSymbol;
this.newSymbol = seSymbol;
} else {
// Already a new symbol, set it as original first
if (this.newSymbol != null) {
this.originalSymbol = this.newSymbol;
}
this.newSymbol = seSymbol;
}
// Update SQL Conversion Data - based on symbol and attr type
updateSqlConversionData();
}
/**
* set new target attribute type
*
* @param typeStr the new type String
*/
public void setNewAttrDatatype( EObject datatype ) {
newAttrDatatype = datatype;
// Update the SQL Conversion Data - based on new attribute runtime type
updateSqlConversionData();
}
/**
* set new target attribute name
*
* @param name the new name String
*/
public void setNewAttrName( String name ) {
newAttrName = name;
}
/**
* get new target attribute type
*
* @return the new type String
*/
public EObject getOriginalAttrDatatype() {
return newAttrDatatype;
}
/**
* get current target attribute type
*
* @return the new type String
*/
public EObject getCurrentAttrDatatype() {
EObject result = originalAttrDatatype;
if (hasAttrTypeModification()) {
result = newAttrDatatype;
}
return result;
}
/**
* get current target attribute name
*
* @return the attribute name
*/
public String getCurrentAttrName() {
if (hasAttrNameModification()) {
return newAttrName;
}
return getAttributeName();
}
/**
* Check whether the target attribute type has been modified
*
* @return true if there is a pending modification, false if not.
*/
public boolean hasAttrTypeModification() {
return (newAttrDatatype != null);
}
/**
* Check whether the target attribute name has been modified
*
* @return true if there is a pending modification, false if not.
*/
public boolean hasAttrNameModification() {
return (newAttrName != null);
}
public void applyAttrNameModification( Object txnSource ) {
if (hasAttrNameModification()) {
String newName = getCurrentAttrName();
Object attribute = getAttribute();
if (attribute instanceof EObject) {
TransformationHelper.setSqlColumnName((EObject)getAttribute(), newName, txnSource);
}
}
}
public void applyAttrTypeModification( Object txnSource ) {
if (hasAttrTypeModification()) {
EObject newType = getCurrentAttrDatatype();
Object attribute = getAttribute();
if (attribute instanceof EObject) {
TransformationHelper.setSqlColumnDatatype((EObject)attribute, newType, txnSource);
}
}
}
/**
* Check whether the SqlSymbol has been modified
*
* @return true if there is a pending modification, false if not.
*/
public boolean hasSymbolModification() { // NO_UCD
return (newSymbol != null);
}
/**
* Check whether the SqlSymbol has been modified
*
* @return true if there is a pending modification, false if not.
*/
public boolean hasAvailableSymbolConversion() {
return (availableSymbolConversion != null);
}
/**
* accept the sql Conversion. This resets the sqlSymbol to the new Symbol
*
* @param seSymbol the new Sql Symbol
*/
public void acceptSqlConversion() {
if (hasAvailableSymbolConversion()) {
newSymbol = availableSymbolConversion;
// Update the SQL Conversion Data - based on attribute runtime type and new symbol
updateSqlConversionData();
// track that the original symbol was modified
sqlWasConverted = true;
}
}
/**
* undo the sql Conversion after it has been accepted. This resets the new Symbolto null.
*/
public void undoSqlConversion() {
if (sqlWasConverted) {
newSymbol = null;
// Update the SQL Conversion Data - based on attribute runtime type and new symbol
updateSqlConversionData();
// track that the original symbol was modified
sqlWasConverted = false;
}
}
/**
* Check whether there are any modifications to the SQL Symbols
*
* @return true if there are pending modifications, false if not.
*/
public boolean sqlSymbolWasConverted() {
return sqlWasConverted;
}
/**
* accept the attribute type conversion. This resets the sqlSymbol to the new Symbol
*
* @param seSymbol the new Sql Symbol
*/
public void acceptAttributeConversion() {
if (hasAttributeConversion()) {
// Get current symbols runtime type
String runtimeType = getCurrentSymbolRuntimeType();
// Get default datatype for it
EObject datatype = TransformationMappingHelper.getDefaultDatatypeForRuntimeTypeName(runtimeType);
// set the attribute datatype
setNewAttrDatatype(datatype);
}
}
/**
* Get attribute Name
*
* @return the attribute name
*/
public String getAttributeName() {
String result = PluginConstants.EMPTY_STRING;
Object attr = getAttribute();
if (TransformationHelper.isSqlColumn(attr)) {
result = TransformationHelper.getSqlEObjectName((EObject)attr);
} else if (attr instanceof String) {
result = (String)attr;
}
return result;
}
/**
* Get attribute Full Name
*
* @return the attribute full name
*/
public String getAttributeFullName() {
String result = PluginConstants.EMPTY_STRING;
Object attr = getAttribute();
if (TransformationHelper.isSqlColumn(attr)) {
result = TransformationHelper.getSqlEObjectFullName((EObject)attr);
} else if (attr instanceof String) {
result = (String)attr;
}
return result;
}
/**
* Get attribute String description text - "name : currentType"
*
* @return the string descriptive text
*/
public String getAttributeText( boolean showType ) {
StringBuffer sb = new StringBuffer(getCurrentAttrName());
if (showType) {
final EObject currentDatatype = getCurrentAttrDatatype();
final DatatypeManager dtMgr = ModelerCore.getDatatypeManager(currentDatatype, true);
final String dtName = dtMgr.getRuntimeTypeName(currentDatatype);
if (dtName != null) {
sb.append(" : " + dtName); //$NON-NLS-1$
}
}
return sb.toString();
}
/**
* Get SqlSymbol description text for the current symbol
*
* @return the string descriptive text
*/
public String getSqlSymbolText( boolean showType ) {
StringBuffer sb = new StringBuffer();
Object sqlSymbol = getCurrentSymbol();
if (sqlSymbol != null) {
// If its an IAliasSymbol, use underlying symbol
if (sqlSymbol instanceof IAliasSymbol) {
sqlSymbol = ((IAliasSymbol)sqlSymbol).getSymbol();
}
if (sqlSymbol instanceof IExpression) {
String symbolName = TransformationSqlHelper.getSingleElementSymbolShortName((IExpression)sqlSymbol, true);
// show aliased if necessary
if (!isInputParamBinding()) {
String attrName = getCurrentAttrName();
String symShortName = TransformationSqlHelper.getSingleElementSymbolShortName((IExpression)sqlSymbol,
false);
// If symbol and attribute shortNames are different, show as aliased
if (!attrName.equalsIgnoreCase(symShortName)) {
sb = new StringBuffer(symbolName + SPACE + AS + SPACE + attrName);
// same names, just show symbol
} else {
sb = new StringBuffer(symbolName);
}
// InputParam Bindings are never aliased
} else {
sb = new StringBuffer(symbolName);
}
if (showType) {
sb.append(" : " + getSymbolDatatype((IExpression)sqlSymbol)); //$NON-NLS-1$
}
}
}
return sb.toString();
}
private String getSymbolDatatype( IExpression seSymbol ) {
String typeName;
Class objClass = seSymbol.getType();
IDataTypeManagerService service = ModelerCore.getTeiidDataTypeManagerService();
if (objClass == null) {
typeName = service.getDataTypeName(NullType.class);
} else {
typeName = service.getDataTypeName(objClass);
}
return typeName;
}
/**
* Create a SingleElementSymbol for the current state of the binding Create from the Current Binding symbol and ensure it will
* match the current name.
*
* @return the SingleElementSymbol for the current state of the binding
*/
public IExpression createBindingSymbol() {
IExpression result = null;
if (isBound()) {
IExpression currentSymbol = getCurrentSymbol();
String currentAttrName = getCurrentAttrName();
String symbolShortName = TransformationSqlHelper.getSingleElementSymbolShortName(currentSymbol, false);
// -----------------------------------------------------------------
// Name of current Symbol matches the target Attribute
// -----------------------------------------------------------------
if (symbolShortName != null && symbolShortName.equalsIgnoreCase(currentAttrName)) {
result = (IExpression)currentSymbol.clone();
// -----------------------------------------------------------------
// Name of sql doesnt match the target Attribute
// -----------------------------------------------------------------
} else {
// If this is already an alias, rename it
if (currentSymbol instanceof IAliasSymbol) {
IExpression uSymbol = ((IAliasSymbol)currentSymbol).getSymbol();
// If underlying symbol matches, drop the alias
IQueryService queryService = ModelerCore.getTeiidQueryService();
if (queryService.getSymbolShortName(uSymbol).equalsIgnoreCase(currentAttrName)) {
result = (IExpression)uSymbol.clone();
} else {
IAliasSymbol aSym = (IAliasSymbol)currentSymbol.clone();
aSym.setShortName(currentAttrName);
result = aSym;
}
// If this is not an alias, make it one
} else {
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
currentSymbol = factory.createAliasSymbol(currentAttrName, currentSymbol);
result = currentSymbol;
}
}
}
return result;
}
/**
* Determine if there is an attribute conversion for this binding. Checks to see if the attribute has a type which is
* compatible with the current SQL Symbol type.
*
* @return 'true' if the attribute conversion is possible, 'false' if not.
*/
public boolean hasAttributeConversion() {
boolean canConvert = false;
// Get current symbol runtime type
String symbolRuntimeType = getCurrentSymbolRuntimeType();
// Get default datatype compatible with symbol runtime type
if (symbolRuntimeType != null) {
EObject datatype = TransformationMappingHelper.getDefaultDatatypeForRuntimeTypeName(symbolRuntimeType);
// If a conversion is available, canConvert = true
if (datatype != null) {
canConvert = true;
}
}
return canConvert;
}
/**
* Get the SQL Conversion status for this binding
*
* @return 'true' if the Sql conversion is possible, 'false' if not.
*/
public boolean canConvertSqlSymbol() {
return (availableSymbolConversion != null);
}
/**
* Get the SQL Conversion display text for the selected binding
*
* @return the Symbol Conversion text
*/
public String getSqlConversionText() {
return sqlConversionText;
}
private void updateSqlConversionData() {
updateCanConvertSqlStatus();
updateAvailableSymbolConversionAndText();
}
/**
* Updates whether the binding's SQL symbol can be converted so that it's runtime type matches the attribute type.
*/
private void updateCanConvertSqlStatus() {
// canConvertSql=false;
// If the current attribute is a "String", this is binding for new attribute
// or if the current attribute datatype is null, will take on datatype of symbol
// Set canConvert true
if (getAttribute() instanceof String || getCurrentAttrDatatype() == null) {
// canConvertSql=true;
return;
}
}
/**
* update the SQL Conversion display text and symbol for this binding
*/
public void updateAvailableSymbolConversionAndText() {
IDataTypeManagerService service = ModelerCore.getTeiidDataTypeManagerService();
IQueryService queryService = ModelerCore.getTeiidQueryService();
IQueryFactory factory = queryService.createQueryFactory();
sqlConversionText = null;
availableSymbolConversion = null;
if (getCurrentSymbol() == null) {
sqlConversionText = BINDING_SYMBOL_NULL_TEXT;
return;
}
String symbolAlias = "aliasName"; //$NON-NLS-1$
// ----------------------------------------------------
// Get the DataType Name for the current SQL Symbol
// ----------------------------------------------------
EObject attrDatatype = getCurrentAttrDatatype();
if (attrDatatype == null) {
sqlConversionText = NO_CONVERSION_REQD_TEXT + CR + ATTRIBUTE_TYPE_NULL_TEXT;
return;
}
String currentAttrTypeStr = RuntimeTypeConverter.getRuntimeType(attrDatatype);
Object sqlSymbol = getCurrentSymbol();
String sqlSymbolTypeStr = RuntimeTypeConverter.getRuntimeType(sqlSymbol);
IExpression oSymbol = originalSymbol;
// -------------------------------------------------------
// DataTypes not equal, update the SQL to do the CONVERT
// -------------------------------------------------------
if (!currentAttrTypeStr.equalsIgnoreCase(sqlSymbolTypeStr)) {
// if the conversion is desired back to the original SQL type, no convert is required
String originalSQLTypeStr = service.getDataTypeName(oSymbol.getType());
if (currentAttrTypeStr.equalsIgnoreCase(originalSQLTypeStr)) {
availableSymbolConversion = null;
// optimizer.optimize(oSymbol);
sqlConversionText = CONVERT_SQL_TEXT + CR + oSymbol.toString();
} else {
// Check whether there is a conversion
boolean isExplicit = service.isExplicitConversion(sqlSymbolTypeStr, currentAttrTypeStr);
boolean isImplicit = service.isImplicitConversion(sqlSymbolTypeStr, currentAttrTypeStr);
// Explicit conversion, use it
if (isImplicit || isExplicit) {
// If symbol is aliased, get underlying symbol
if (oSymbol instanceof IAliasSymbol) {
IAliasSymbol aSym = (IAliasSymbol)oSymbol;
oSymbol = aSym.getSymbol();
// Symbol alias should be target attribute name
symbolAlias = getCurrentAttrName();
}
if (oSymbol instanceof IElementSymbol) {
symbolAlias = getCurrentAttrName();
IAliasSymbol aSymbol = TransformationSqlHelper.convertElementSymbol((IElementSymbol)oSymbol,
currentAttrTypeStr,
symbolAlias);
availableSymbolConversion = aSymbol;
sqlConversionText = getSQLLabelText(aSymbol, symbolAlias, isExplicit);
return;
} else if (oSymbol instanceof IExpressionSymbol) {
IExpressionSymbol eSymbol = (IExpressionSymbol)oSymbol.clone();
// First check whether the IExpression is a ConvertFunction, and the converted
// Symbol type matches the desired type.
if (TransformationSqlHelper.isConvertFunction(eSymbol)) {
IExpression cExpr = TransformationSqlHelper.getConvertedExpr(eSymbol);
if (cExpr != null) {
IExpression seSymbol = cExpr;
String seSymbolTypeStr = service.getDataTypeName(seSymbol.getType());
if (seSymbolTypeStr != null && seSymbolTypeStr.equalsIgnoreCase(currentAttrTypeStr)) {
availableSymbolConversion = seSymbol;
// optimizer.optimize(availableSymbolConversion);
sqlConversionText = USE_SQL_SYMBOL_TEXT + CR + seSymbol.toString();
// optimizer.deoptimize(availableSymbolConversion);
return;
}
} else {
IExpressionSymbol exprSymbol = factory.createExpressionSymbol("expr", cExpr); //$NON-NLS-1$
availableSymbolConversion = exprSymbol;
if (symbolAlias != null) {
sqlConversionText = USE_SQL_EXPRESSION_TEXT + CR + availableSymbolConversion.toString()
+ SPACE + AS + SPACE + symbolAlias;
} else {
sqlConversionText = USE_SQL_EXPRESSION_TEXT + CR + availableSymbolConversion.toString();
}
eSymbol = TransformationSqlHelper.convertExpressionSymbol(exprSymbol, currentAttrTypeStr);
}
} else {
eSymbol = TransformationSqlHelper.convertExpressionSymbol((IExpressionSymbol)oSymbol,
currentAttrTypeStr);
}
sqlConversionText = getSQLLabelText(eSymbol, symbolAlias, isExplicit);
if (symbolAlias != null) {
availableSymbolConversion = factory.createAliasSymbol(symbolAlias, eSymbol);
} else {
availableSymbolConversion = eSymbol;
}
}
sqlConversionText = CONVERT_SQL_TEXT + SPACE + FROM + SPACE + sqlSymbolTypeStr + SPACE + TO + SPACE
+ currentAttrTypeStr;
// No conversion available
} else {
sqlConversionText = CONVERT_SQL_TEXT + CR + NO_CONVERSION_AVAIL_TEXT + SPACE + FROM + SPACE
+ sqlSymbolTypeStr + SPACE + TO + SPACE + currentAttrTypeStr;
}
}
// -------------------------------------------------------
// DataTypes not equal, update the SQL to do the CONVERT
// -------------------------------------------------------
} else {
sqlConversionText = RUNTIME_TYPES_EQUAL_TEXT + CR + NO_CONVERSION_REQD_TEXT;
}
return;
}
private String getSQLLabelText( IExpression seSymbol,
String symbolAlias,
boolean isExplicit ) {
StringBuffer sb = new StringBuffer();
if (seSymbol != null) {
// optimizer.optimize(seSymbol);
sb.append(seSymbol.toString());
// optimizer.deoptimize(seSymbol);
// If symbolAlias was passed in, use it
if (symbolAlias != null && !(seSymbol instanceof IAliasSymbol)) {
sb.append(SPACE + AS + SPACE + symbolAlias);
}
// If explicit conversion, warn loss of precision
if (isExplicit) {
sb.append(CR + LOSS_OF_PRECISION_WARNING);
}
}
return sb.toString();
}
}