/* * 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.util; import java.util.ArrayList; import java.util.List; import javax.lang.model.type.NullType; import org.eclipse.emf.ecore.EObject; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.metamodel.aspect.AspectManager; import org.teiid.designer.core.metamodel.aspect.sql.SqlColumnAspect; import org.teiid.designer.core.types.DatatypeManager; import org.teiid.designer.query.sql.lang.IExpression; import org.teiid.designer.type.IDataTypeManagerService; /** * The <code>RuntimeTypeConverter</code> class reconciles the runtime types of 2 lists. * * @author Dan Florian * @since 8.0 * @version 1.0 */ public class RuntimeTypeConverter { // ///////////////////////////////////////////////////////////////////////////////////////////// // FIELDS // ///////////////////////////////////////////////////////////////////////////////////////////// /** Indicates if the candidates are compatible. */ private boolean[] compatible; /** Indicates if the corresponding elements of each list have the same runtime type. */ private boolean explicitMatch; /** One set of candidate objects. The other set is called <code>secondaryCandidates</code>. */ private ArrayList primaryCandidates; /** The name to use to describe the primary list if no <code>primaryObj</code> is null. */ private String primaryName = "Primary"; //$NON-NLS-1$ /** The object to use to describe the primary list of candidates. */ private Object primaryObj; /** * The list of results corresponding to the primary candidates. The object at an index will either be the same as the original * list (<code>primaryCandidates</code>) or be a converted value. */ private List primaryResults; /** The size of the primary candidate list. */ private int primarySize; /** One set of candidate objects. The other set is called <code>primaryCandidates</code>. */ private ArrayList secondaryCandidates; /** The name to use to describe the secondary list if <code>secondaryObj</code> is null. */ private String secondaryName = "Secondary"; //$NON-NLS-1$ /** The object to use to describe the secondary list of candidates. */ private Object secondaryObj; /** * The list of results corresponding to the secondary candidates. The object at an index will either be the same as the * original list (<code>secondaryCandidates</code>) or be a converted value. */ private List secondaryResults; /** The size of the secondary candidate list. */ private int secondarySize; /** The size of the largest input list (<code>Math.max(primarySize, secondarySize)</code>). */ private int size; // ///////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS // ///////////////////////////////////////////////////////////////////////////////////////////// /** * Constructs a <code>RuntimeTypeConverter</code> with both list being unlocked. * * @param theEditor the editor to use with <code>MetaObject</code>s * @param theTypeResolver the type resolver to use * @param thePrimaryCandidates the first list of candidates for conversion * @param theSecondaryCandidates the second list of candidates for conversion */ private RuntimeTypeConverter( List thePrimaryCandidates, List theSecondaryCandidates ) { this(thePrimaryCandidates, false, theSecondaryCandidates, false); } /** * Constructs a <code>RuntimeTypeConverter</code>. * * @param theEditor the editor to use with <code>MetaObject</code>s * @param theTypeResolver the type resolver to use * @param thePrimaryCandidates the first list of candidates for conversion * @param thePrimaryCandidatesLockedFlag indicates if the primary list is locked * @param theSecondaryCandidates the second list of candidates for conversion * @param theSecondaryCandidatesLockedFlag indicates if the secondary list is locked */ private RuntimeTypeConverter( List thePrimaryCandidates, boolean thePrimaryCandidatesLockedFlag, List theSecondaryCandidates, boolean theSecondaryCandidatesLockedFlag ) { if ((thePrimaryCandidates == null) || (theSecondaryCandidates == null)) { throw new IllegalArgumentException("RuntimeTypeConverter.init:Primary or secondary candidate list is null."); //$NON-NLS-1$ } primarySize = thePrimaryCandidates.size(); secondarySize = theSecondaryCandidates.size(); size = Math.max(primarySize, secondarySize); primaryCandidates = new ArrayList(primarySize); primaryCandidates.addAll(thePrimaryCandidates); primaryResults = (List)primaryCandidates.clone(); secondaryCandidates = new ArrayList(secondarySize); secondaryCandidates.addAll(theSecondaryCandidates); secondaryResults = (List)secondaryCandidates.clone(); compatible = new boolean[size]; explicitMatch = false; setCompatibility(); setExplicitMatch(); } // ///////////////////////////////////////////////////////////////////////////////////////////// // METHODS // ///////////////////////////////////////////////////////////////////////////////////////////// /** * Gets either the converted object if it exists or the original list item. * * @param theIndex the index of the object being requested * @param thePrimaryFlag a flag indicating if the object is from the primary or secondary list * @return the requested result object */ private Object getCandidateResultAt( int theIndex, boolean thePrimaryFlag ) { validateIndex(theIndex, (thePrimaryFlag) ? primarySize : secondarySize, "getCandidateResultAt(int, boolean"); //$NON-NLS-1$ List candidates = (thePrimaryFlag) ? primaryResults : secondaryResults; return candidates.get(theIndex); } private String getName( boolean thePrimaryFlag ) { String result = (thePrimaryFlag) ? primaryName : secondaryName; Object obj = (thePrimaryFlag) ? primaryObj : secondaryObj; if (obj != null && obj instanceof EObject && TransformationHelper.isSqlColumn(obj)) { EObject eObj = (EObject)obj; SqlColumnAspect columnAspect = ((SqlColumnAspect)AspectManager.getSqlAspect(eObj)); result = columnAspect.getFullName(eObj); } return result; } /** * Gets a name derived from either the list object being set or the list name. * * @return the name associated with the primary candidate list * @see #setPrimaryObject(Object) * @see #setPrimaryName(String) */ private String getPrimaryName() { return getName(true); } /** * Gets an object from the result list associated with the original primary list. The object returned can be the same object * as the original primary list, or it can be another object which is the result of a conversion taken place. * * @param theIndex the index of the object to be retrieved * @return the requested object * @see #commit() */ private Object getPrimaryResultAt( int theIndex ) { return getCandidateResultAt(theIndex, true); } /** * Gets the runtime type associated with the given object. These types are defined by the DataTypeManager. * * @param theObj the object whose runtime type is being requested * @return the requested runtime type */ public static String getRuntimeType( Object theObj ) { String type = null; IDataTypeManagerService service = ModelerCore.getTeiidDataTypeManagerService(); if (theObj instanceof EObject) { EObject eObj = (EObject)theObj; final DatatypeManager dtMgr = ModelerCore.getDatatypeManager(eObj, true); if (TransformationHelper.isSqlColumn(theObj)) { SqlColumnAspect columnAspect = (SqlColumnAspect)AspectManager.getSqlAspect(eObj); type = columnAspect.getRuntimeType(eObj); } else if (dtMgr.isSimpleDatatype((EObject)theObj)) { return dtMgr.getRuntimeTypeName(eObj); } } else if (theObj instanceof IExpression) { Class objClass = ((IExpression)theObj).getType(); if (objClass == null) { type = service.getDataTypeName(NullType.class); } else { type = service.getDataTypeName(objClass); } } else { throw new IllegalArgumentException( "RuntimeTypeConverter.getRuntimeType:Object type cannot be determined for <" + theObj + ">."); //$NON-NLS-1$ //$NON-NLS-2$ } return type; } /** * Gets a name derived from either the list object being set or the list name. * * @return the name associated with the secondary candidate list * @see #setSecondaryObject(Object) * @see #setSecondaryName(String) */ private String getSecondaryName() { return getName(false); } /** * Gets an object from the result list associated with the original secondary list. The object returned can be the same object * as the original secondary list, or it can be another object which is the result of a conversion taken place. * * @param theIndex the index of the object to be retrieved * @return the requested object * @see #commit() */ private Object getSecondaryResultAt( int theIndex ) { return getCandidateResultAt(theIndex, false); } private boolean isCompatible( String theFirstType, String theSecondType ) { IDataTypeManagerService service = ModelerCore.getTeiidDataTypeManagerService(); return (theFirstType.equals(theSecondType) || service.isTransformable(theFirstType, theSecondType)); } /** * Static Method to determine if the runtime types of two items exactly match. * * @param primaryObj the primary Object * @param secondaryObj the secondary Object * @return <code>true</code> if items match; <code>false</code> otherwise. */ public static boolean isExplicitMatch( Object primaryObj, Object secondaryObj ) { boolean isExplicitMatch = false; if (primaryObj != null && secondaryObj != null) { List primaryList = new ArrayList(1); List secondaryList = new ArrayList(1); primaryList.add(primaryObj); secondaryList.add(secondaryObj); RuntimeTypeConverter rtc = new RuntimeTypeConverter(primaryList, true, secondaryList, true); isExplicitMatch = rtc.isExplicitMatch(); } return isExplicitMatch; } /** * Indicates of the runtime types of the associated item in each list exactly match. * * @return <code>true</code> if list items match; <code>false</code> otherwise. */ private boolean isExplicitMatch() { return explicitMatch; } /** * Indicates of the runtime types of the associated item at the given index in each list exactly match. * * @return <code>true</code> if types match; <code>false</code> otherwise. */ private boolean isExplicitMatch( int theIndex ) { validateIndex(theIndex, size, "isExplicitMatch(int)"); //$NON-NLS-1$ boolean result = false; if ((theIndex < primarySize) && (theIndex < secondarySize)) { Object primaryCandidate = primaryCandidates.get(theIndex); String primaryType = getRuntimeType(primaryCandidate); Object secondaryCandidate = secondaryCandidates.get(theIndex); String secondaryType = getRuntimeType(secondaryCandidate); result = primaryType.equals(secondaryType); } return result; } private void setCompatibility() { for (int i = 0; i < size; i++) { setCompatibility(i); } } private void setCompatibility( int theIndex ) { validateIndex(theIndex, size, "setCompatibility(int)"); //$NON-NLS-1$ boolean result = false; if ((theIndex < primarySize) && (theIndex < secondarySize)) { Object primary = getPrimaryResultAt(theIndex); String primaryType = getRuntimeType(primary); Object secondary = getSecondaryResultAt(theIndex); String secondaryType = getRuntimeType(secondary); result = (isCompatible(primaryType, secondaryType) || isCompatible(secondaryType, primaryType)); } compatible[theIndex] = result; } private void setExplicitMatch() { // don't call setExplicitMatch(int) since it also loops up to "size" times boolean match = true; for (int i = 0; i < size; i++) { if (!isExplicitMatch(i)) { match = false; break; } } explicitMatch = match; } /** * Gets both list names with a delimeter between them. * * @return a concatenation of the list names */ @Override public String toString() { return getPrimaryName() + "<->" + getSecondaryName(); //$NON-NLS-1$ } private void validateIndex( int theIndex, int theSize, String theMethod ) { if (theSize <= theIndex) { throw new IllegalArgumentException(new StringBuffer().append(getClass().getName()).append(".") //$NON-NLS-1$ .append(theMethod).append(":Index <") //$NON-NLS-1$ .append(theIndex).append("> is invalid for size of <") //$NON-NLS-1$ .append(theSize).append(">.") //$NON-NLS-1$ .toString()); } } }