/*

   Derby - Class org.apache.derby.impl.sql.compile.CastNode

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to you under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package	org.apache.derby.impl.sql.compile;

import java.lang.reflect.Modifier;
import java.sql.Types;
import java.util.List;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.shared.common.reference.ClassName;
import org.apache.derby.shared.common.reference.Limits;
import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derby.iapi.services.classfile.VMOpcode;
import org.apache.derby.iapi.services.compiler.LocalField;
import org.apache.derby.iapi.services.compiler.MethodBuilder;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.sql.compile.TypeCompiler;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.DataTypeUtilities;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.NumberDataType;
import org.apache.derby.iapi.types.TypeId;
import org.apache.derby.iapi.util.JBitSet;
import org.apache.derby.iapi.util.StringUtil;

/**
 * An CastNode represents a cast expression.
 *
 */

class CastNode extends ValueNode
{
	ValueNode			castOperand;
	private int					targetCharType;
	TypeId	sourceCTI = null;
	private boolean forDataTypeFunction = false;

    /** The original, unbound descriptor for the target type, if it is a UDT. */
    private DataTypeDescriptor targetUDT;

    /** This variable gets set by the parser to indicate that this CAST node
	 * has been generated by the parser. This means that we should use the 
     * collation info of the current compilation schema for this node's
	 * collation setting. If this variable does not get set to true, then it 
	 * means that this CAST node has been an internally generated node and we 
	 * should not touch the collation info set for this CAST node because it 
	 * has been already set correctly by the class that generated this CAST 
	 * node. Collation info is part of the DataTypeDescriptor that's defined
	 * on the ValueNode (the super class of this CastNode class)
	 */ 
	private boolean externallyGeneratedCastNode = false;

	/*
	** Static array of valid casts.  Dimentions
	** produce a single boolean which indicates
	** whether the case is possible or not.
	*/

    /**
     * Method calls:
     * Argument type has the same semantics as assignment:
     * Section 9.2 (Store assignment). There, General Rule
     * 2.b.v.2 says that the database should raise an exception
     * if truncation occurs when stuffing a string value into a
     * VARCHAR, so make sure CAST doesn't issue warning only.
     */
    private boolean assignmentSemantics = false;

    /**
     * The name of the target type if it's a UDT. It is partly redundant, as
     * the name can also be retrieved from the type descriptor. Additionally,
     * it contains information about the location of the UDT name in the
     * query text, which is useful if the query text needs to be rewritten.
     * (Useful for example when rewriting a CHECK constraint definition to
     * have fully qualified names before storing it in the dictionary.) This
     * field is only set for <b>explicit</b> casts to a UDT.
     */
    private TableName udtTargetName;

	/**
     * Constructor for a CastNode
	 *
	 * @param castOperand	The operand of the node
	 * @param castTarget	DataTypeServices (target type of cast)
     * @param cm            The context manager
	 *
	 * @exception StandardException		Thrown on error
	 */

    CastNode(ValueNode castOperand,
             DataTypeDescriptor castTarget,
             ContextManager cm) throws StandardException {
        super(cm);
        this.castOperand = castOperand;

        // DERBY-6421: setType() tries to bind user defined types. We don't
        // want to do any binding here, since we could be called during
        // parsing. If the target type is a UDT, just store it for now and
        // do the binding later when bindExpression() or bindCastNodeOnly()
        // is called.
        if (castTarget.getTypeId().isUserDefinedTypeId()) {
            targetUDT = castTarget;
        } else {
            setType(castTarget);
        }
	}

	/**
     * Constructor for a CastNode
	 *
	 * @param castOperand	The operand of the node
	 * @param charType		CHAR or VARCHAR JDBC type as target
	 * @param charLength	target type length
     * @param cm            The context manager
	 *
	 * @exception StandardException		Thrown on error
	 */

    CastNode(ValueNode castOperand,
             int charType,
             int charLength,
             ContextManager cm) throws StandardException {
        super(cm);
        this.castOperand = castOperand;
        int charLen = charLength;
        targetCharType = charType;
		if (charLen < 0)	// unknown, figure out later
			return;
		setType(DataTypeDescriptor.getBuiltInDataTypeDescriptor(targetCharType, charLen));
	}

	/**
	 * Convert this object to a String.  See comments in QueryTreeNode.java
	 * for how this should be done for tree printing.
	 *
	 * @return		This object as a String
	 */
    @Override
	public String toString()
	{
	    if (SanityManager.DEBUG)
	    {
	        return "castTarget: " + getTypeServices() + "\n" +
	        super.toString();
	    }
	    else
	    {
	        return "";
	    }
	}

	/**
	 * Prints the sub-nodes of this object.  See QueryTreeNode.java for
	 * how tree printing is supposed to work.
	 *
	 * @param depth		The depth of this node in the tree
	 */
    @Override
    void printSubNodes(int depth)
	{
		if (SanityManager.DEBUG)
		{
			super.printSubNodes(depth);

			if (castOperand != null)
			{
				printLabel(depth, "castOperand: ");
				castOperand.treePrint(depth + 1);
			}
		}
	}
	protected int getOrderableVariantType() throws StandardException
	{
		return castOperand.getOrderableVariantType();
	}

	/**
	 * Bind this expression.  This means binding the sub-expressions,
	 * as well as figuring out what the return type is for this expression.
	 *
	 * @param fromList		The FROM list for the query this
	 *				expression is in, for binding columns.
	 * @param subqueryList		The subquery list being built as we find SubqueryNodes
     * @param aggregates        The aggregate list being built as we find AggregateNodes
	 *
	 * @return	The new top of the expression tree.
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override @SuppressWarnings("fallthrough")
    ValueNode bindExpression(FromList fromList, SubqueryList subqueryList, List<AggregateNode> aggregates)
				throws StandardException
	{
		castOperand = castOperand.bindExpression(
								fromList, subqueryList,
                                aggregates);

        // Bind the target UDT.
        if (targetUDT != null) {
            setType(targetUDT);
        }

		if (getTypeServices() == null)   //CHAR or VARCHAR function without specifying target length
		{
			DataTypeDescriptor opndType = castOperand.getTypeServices();
			int length = -1;
			TypeId srcTypeId = opndType.getTypeId();

            if (srcTypeId.isNumericTypeId())
            {
                length = opndType.getPrecision() + 1; // 1 for the sign
                if (opndType.getScale() > 0)
                    length += 1;               // 1 for the decimal .

            }
            /*
             * Derby-1132 : The length for the target type was calculated
             * incorrectly while Char & Varchar functions were used. Thus
             * adding the check for Char & Varchar and calculating the
             * length based on the operand type.
             */
            else if(srcTypeId.isStringTypeId())
            {
                length = opndType.getMaximumWidth();

                // Truncate the target type width to the max width of the
                // data type
                if (this.targetCharType == Types.CHAR)
                    length = Math.min(length, Limits.DB2_CHAR_MAXWIDTH);
                else if (this.targetCharType == Types.VARCHAR)
                    length = Math.min(length, Limits.DB2_VARCHAR_MAXWIDTH);
            }
            else
            {
                TypeId typeid = opndType.getTypeId();

                if (length < 0) {
                    length = DataTypeUtilities.getColumnDisplaySize(
                        typeid.getJDBCTypeId(), -1);
                }
            }

            if (length < 0)
                length = 1;  // same default as in parser
			setType(DataTypeDescriptor.getBuiltInDataTypeDescriptor(targetCharType, length));
			
		}

		/* 
		** If castOperand is an untyped null, 
		** then we must set the type.
		*/
		if (castOperand instanceof UntypedNullConstantNode)
		{
			castOperand.setType(getTypeServices());
		}

		bindCastNodeOnly();
		
		/* We can't chop out cast above an untyped null because
		 * the store can't handle it.
		 */
		if ((castOperand instanceof ConstantNode) &&
			!(castOperand instanceof UntypedNullConstantNode))
		{
			/* If the castOperand is a typed constant then we do the cast at
			 * bind time and return a constant of the correct type.
			 * NOTE: This could return an exception, but we're prepared to 
			 * deal with that. (NumberFormatException, etc.)
			 * We only worry about the easy (and useful)
			 * converions at bind time.
			 * Here's what we support:
			 *			source					destination
			 *			------					-----------
			 *			boolean					boolean
			 *			boolean					char
			 *			char					boolean
			 *			char					date/time/ts
			 *			char					non-decimal numeric
			 *			date/time/ts			char
			 *			numeric					char
			 *			numeric					non-decimal numeric
			 */
			/* RESOLVE - to be filled in. */
			ValueNode retNode = this;
			int		  sourceJDBCTypeId = sourceCTI.getJDBCTypeId();
			int		  destJDBCTypeId = getTypeId().getJDBCTypeId();

			switch (sourceJDBCTypeId)
			{
				case Types.BIT:
				case Types.BOOLEAN:
					// (BIT is boolean)
					if (destJDBCTypeId == Types.BIT || destJDBCTypeId == Types.BOOLEAN)
					{
						retNode = castOperand;
					}
					else if (destJDBCTypeId == Types.CHAR)
					{
						BooleanConstantNode bcn = (BooleanConstantNode) castOperand;
						String booleanString = bcn.getValueAsString();
                       retNode = new CharConstantNode(
                                booleanString,
                                getTypeServices().getMaximumWidth(),
                                getContextManager());
					}
					break;

					case Types.CHAR:
						retNode = getCastFromCharConstant(destJDBCTypeId);
						break;

					case Types.DATE:
					case Types.TIME:
					case Types.TIMESTAMP:
						if (destJDBCTypeId == Types.CHAR)
						{
							String castValue =  
								((UserTypeConstantNode) castOperand).
											getObjectValue().
												toString();
                           retNode = new CharConstantNode(
                               castValue,
                               getTypeServices().getMaximumWidth(),
                               getContextManager());
						}
						break;

					case Types.DECIMAL:
						// ignore decimal -> decimal casts for now
						if (destJDBCTypeId == Types.DECIMAL ||
							destJDBCTypeId == Types.NUMERIC)
							break;
						// fall through
					case Types.TINYINT:
					case Types.SMALLINT:
					case Types.INTEGER:
					case Types.BIGINT:
					case Types.DOUBLE:
					case Types.REAL:
						retNode = getCastFromNumericType(
											((ConstantNode) castOperand).getValue(), 
											destJDBCTypeId);
						break;

			}

			// Return the new constant if the cast was performed
			return retNode;
		}

		return this;
	}

	/**
	 * Bind this node but not its child.  Caller has already bound
	 * the child.
	 * This is useful for when we generate a CastNode during binding
	 * after having already bound the child.
	 *
	 * @exception StandardException		Thrown on error
	 */
    void bindCastNodeOnly()
		throws StandardException
	{
        // Bind the target UDT.
        if (targetUDT != null) {
            setType(targetUDT);
        }

		/*
		** The result type is always castTarget.
		*/
		sourceCTI = castOperand.getTypeId();

		//If the result type of cast is string data type, then that data type 
		//should get it's collation type from the current schema. 
		if (externallyGeneratedCastNode && getTypeId().isStringTypeId()) {
			//set the collation type to be same as the compilation schema's 
			//collation type. Collation derivation will be set to "IMPLICIT".
			setCollationUsingCompilationSchema();
		}
		/* 
		** If it is a java cast, do some work to make sure
		** the classes are ok and that they are compatible
		*/
		if (getTypeId().userType())
		{
            setType( bindUserType( getTypeServices() ) );
            
			String className = getTypeId().getCorrespondingJavaTypeName();

			verifyClassExist(className);
		}

        // Set the schema name of the UDT target type.
        if (udtTargetName != null) {
            udtTargetName.setSchemaName(
                    getTypeId().getBaseTypeId().getSchemaName());
        }

        // Obviously the type of a parameter that
        // requires its type from context (a parameter)
        // gets its type from the type of the CAST.
		if (castOperand.requiresTypeFromContext())
		{
            castOperand.setType(getTypeServices());
		}

		/*
		** If it isn't null, then we have
		** a cast from one JBMS type to another.  So we
		** have to figure out if it is legit.
		*/
		else if (!(castOperand instanceof UntypedNullConstantNode))
		{
			/*
			** Make sure we can assign the two classes
			*/
			TypeCompiler tc = castOperand.getTypeCompiler();
			if (! tc.convertible(getTypeId(), forDataTypeFunction))
			{
				throw StandardException.newException(SQLState.LANG_INVALID_CAST, 
						sourceCTI.getSQLTypeName(),
                        getTypeId().getSQLTypeName());
			}
		}	

        //
		// Preserve the nullability of the operand since a CAST
		// of a non-NULL value is also non-NULL. However, if the source type is
        // a non-nullable string type and the target type is a boolean, then the result
        // still must be nullable because the string "unknown" casts to boolean NULL.
        //
        if (
            castOperand.getTypeServices().getTypeId().isStringTypeId() &&
            getTypeId().isBooleanTypeId()
            )
        { setNullability( true ); }
		else { setNullability(castOperand.getTypeServices().isNullable()); }
        
        if (targetUDT != null)
        {
            addUDTUsagePriv( this );
        }
	}

	/**
	 * Get a constant representing the cast from a CHAR to another
	 * type.  If this is not an "easy" cast to perform, then just
	 * return this cast node.
	 * Here's what we think is "easy":
	 *			source			destination
	 *			------			-----------
	 *			char			boolean
	 *			char			date/time/ts
	 *			char			non-decimal numeric
	 *
	 * @param destJDBCTypeId	The destination JDBC TypeId
	 *
	 * @return The new top of the tree (this CastNode or a new Constant)
	 *
	 * @exception StandardException		Thrown on error
	 */
	private ValueNode getCastFromCharConstant(int destJDBCTypeId)
		throws StandardException
	{
		String	  charValue = ((CharConstantNode) castOperand).getString();
		String	  cleanCharValue = StringUtil.SQLToUpperCase(charValue.trim());
		ValueNode retNode = this;

		switch (destJDBCTypeId)
		{
			case Types.BIT:
			case Types.BOOLEAN:
				if (cleanCharValue.equals("TRUE"))
				{
                   return new BooleanConstantNode(true, getContextManager());
				}
				else if (cleanCharValue.equals("FALSE"))
				{
                   return new BooleanConstantNode(false, getContextManager());
				}
				else if (cleanCharValue.equals("UNKNOWN"))
				{
                   return new BooleanConstantNode(getContextManager());
				}
				else
				{
					throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, "boolean");
				}

			case Types.DATE:
               return new UserTypeConstantNode(
										getDataValueFactory().getDateValue(cleanCharValue, false),
										getContextManager());

			case Types.TIMESTAMP:
               return new UserTypeConstantNode(
									getDataValueFactory().getTimestampValue(cleanCharValue, false),
									getContextManager());

			case Types.TIME:
               return new UserTypeConstantNode(
										getDataValueFactory().getTimeValue(cleanCharValue, false),
										getContextManager());

			case Types.TINYINT:
			case Types.SMALLINT:
			case Types.INTEGER:
			case Types.BIGINT:				
				try 
				{
					// #3756 - Truncate decimal portion for casts to integer
                  return getCastFromIntegralType((long) Double.parseDouble(cleanCharValue),
												   destJDBCTypeId);
				}
				catch (NumberFormatException nfe)
				{
					String sqlName = TypeId.getBuiltInTypeId(destJDBCTypeId).getSQLTypeName();
					throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, sqlName);
				}
			case Types.REAL:
				Float floatValue;
				try
				{
					floatValue = Float.valueOf(cleanCharValue);
				}
				catch (NumberFormatException nfe)
				{
					throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, "float");
				}
               return new NumericConstantNode(
                       TypeId.getBuiltInTypeId(Types.REAL),
                       floatValue,
                       getContextManager());
			case Types.DOUBLE:
				Double doubleValue;
				try
				{
					doubleValue = Double.parseDouble(cleanCharValue);
				}
				catch (NumberFormatException nfe)
				{
					throw StandardException.newException(SQLState.LANG_FORMAT_EXCEPTION, "double");
				}
               return new NumericConstantNode(
                       TypeId.getBuiltInTypeId(Types.DOUBLE),
                       doubleValue,
                       getContextManager());
		}

		return retNode;
	}


	/**
	 * Get a constant representing the cast from an integral type to another
	 * type.  If this is not an "easy" cast to perform, then just
	 * return this cast node.
	 * Here's what we think is "easy":
	 *			source				destination
	 *			------				-----------
	 *			integral type		 non-decimal numeric
	 *			integral type		 char
	 *
	 * @param longValue			integral type as a long to cast from
	 * @param destJDBCTypeId	The destination JDBC TypeId
	 *
	 * @return The new top of the tree (this CastNode or a new Constant)
	 *
	 * @exception StandardException		Thrown on error
	 */
	private ValueNode getCastFromIntegralType(
									  long longValue, 
									  int destJDBCTypeId)
		throws StandardException
	{
		ValueNode retNode = this;

		switch (destJDBCTypeId)
		{
			case Types.CHAR:
               return new CharConstantNode(
                        Long.toString(longValue),
                        getTypeServices().getMaximumWidth(),
                        getContextManager());
			case Types.TINYINT:
				if (longValue < Byte.MIN_VALUE ||
					longValue > Byte.MAX_VALUE)
				{
					throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "TINYINT");
				}
               return new NumericConstantNode(
                       TypeId.getBuiltInTypeId(Types.TINYINT),
                       (byte) longValue,
                       getContextManager());

			case Types.SMALLINT:
				if (longValue < Short.MIN_VALUE ||
					longValue > Short.MAX_VALUE)
				{
					throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "SHORT");
				}
               return new NumericConstantNode(
                   TypeId.getBuiltInTypeId(destJDBCTypeId),
                   (short) longValue,
                   getContextManager());

			case Types.INTEGER:
				if (longValue < Integer.MIN_VALUE ||
					longValue > Integer.MAX_VALUE)
				{
					throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "INTEGER");
				}
               return new NumericConstantNode(
                   TypeId.getBuiltInTypeId(destJDBCTypeId),
                   (int) longValue,
                   getContextManager());

			case Types.BIGINT:
               return new NumericConstantNode(
                   TypeId.getBuiltInTypeId(destJDBCTypeId),
                   longValue,
                   getContextManager());

			case Types.REAL:
				if (Math.abs(longValue) > Float.MAX_VALUE)
				{
					throw StandardException.newException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, "REAL");
				}
               return new NumericConstantNode(
                   TypeId.getBuiltInTypeId(destJDBCTypeId),
                   (float) longValue,
                   getContextManager());

			case Types.DOUBLE:
               return new NumericConstantNode(
                   TypeId.getBuiltInTypeId(destJDBCTypeId),
                   (double) longValue,
                   getContextManager());
		}

		return retNode;
	}

	/**
	 * Get a constant representing the cast from a non-integral type to another
	 * type.  If this is not an "easy" cast to perform, then just
	 * return this cast node.
	 * Here's what we think is "easy":
	 *			source				destination
	 *			------				-----------
	 *			non-integral type	 non-decimal numeric
	 *			non-integral type	 char
	 *
	 * @param constantValue		non-integral type a a double to cast from
	 * @param destJDBCTypeId	The destination JDBC TypeId
	 *
	 * @return The new top of the tree (this CastNode or a new Constant)
	 *
	 * @exception StandardException		Thrown on error
	 */
	private ValueNode getCastFromNumericType(
									  DataValueDescriptor constantValue, 
									  int destJDBCTypeId)
		throws StandardException
	{
		switch (destJDBCTypeId)
		{
			case Types.CHAR:
               return new CharConstantNode(
                        constantValue.getString(),
                        getTypeServices().getMaximumWidth(),
                        getContextManager());

			case Types.TINYINT:
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                        Byte.valueOf(constantValue.getByte()),
                        getContextManager());

			case Types.SMALLINT:
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                        Short.valueOf(constantValue.getShort()),
                        getContextManager());

			case Types.INTEGER:
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                        Integer.valueOf(constantValue.getInt()),
                        getContextManager());

			case Types.BIGINT:
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                        Long.valueOf(constantValue.getLong()),
                        getContextManager());

			case Types.REAL:
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                        Float.valueOf(NumberDataType.normalizeREAL(
                                      constantValue.getDouble())),
                        getContextManager());

			case Types.DOUBLE:
				// no need to normalize here because no constant could be out of range for a double
                return new NumericConstantNode(
                        TypeId.getBuiltInTypeId(destJDBCTypeId),
                       Double.valueOf(constantValue.getDouble()),
                        getContextManager());
		}

        return this;
	}

	/**
	 * Preprocess an expression tree.  We do a number of transformations
	 * here (including subqueries, IN lists, LIKE and BETWEEN) plus
	 * subquery flattening.
	 * NOTE: This is done before the outer ResultSetNode is preprocessed.
	 *
	 * @param	numTables			Number of tables in the DML Statement
	 * @param	outerFromList		FromList from outer query block
	 * @param	outerSubqueryList	SubqueryList from outer query block
	 * @param	outerPredicateList	PredicateList from outer query block
	 *
	 * @return		The modified expression
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    ValueNode preprocess(int numTables,
								FromList outerFromList,
								SubqueryList outerSubqueryList,
								PredicateList outerPredicateList) 
					throws StandardException
	{
		castOperand = castOperand.preprocess(numTables,
											 outerFromList, outerSubqueryList,
											 outerPredicateList);
		return this;
	}

	/**
	 * Categorize this predicate.  Initially, this means
	 * building a bit map of the referenced tables for each predicate.
	 * If the source of this ColumnReference (at the next underlying level) 
	 * is not a ColumnReference or a VirtualColumnNode then this predicate
	 * will not be pushed down.
	 *
	 * For example, in:
	 *		select * from (select 1 from s) a (x) where x = 1
	 * we will not push down x = 1.
	 * NOTE: It would be easy to handle the case of a constant, but if the
	 * inner SELECT returns an arbitrary expression, then we would have to copy
	 * that tree into the pushed predicate, and that tree could contain
	 * subqueries and method calls.
	 * RESOLVE - revisit this issue once we have views.
	 *
	 * @param referencedTabs	JBitSet with bit map of referenced FromTables
	 * @param simplePredsOnly	Whether or not to consider method
	 *							calls, field references and conditional nodes
	 *							when building bit map
	 *
	 * @return boolean		Whether or not source.expression is a ColumnReference
	 *						or a VirtualColumnNode.
	 *
	 * @exception StandardException			Thrown on error
	 */
    @Override
    boolean categorize(JBitSet referencedTabs, boolean simplePredsOnly)
		throws StandardException
	{
		return castOperand.categorize(referencedTabs, simplePredsOnly);
	}

	/**
	 * Remap all ColumnReferences in this tree to be clones of the
	 * underlying expression.
	 *
	 * @return ValueNode			The remapped expression tree.
	 *
	 * @exception StandardException			Thrown on error
	 */
    @Override
    ValueNode remapColumnReferencesToExpressions()
		throws StandardException
	{
		castOperand = castOperand.remapColumnReferencesToExpressions();
		return this;
	}

	/**
	 * Return whether or not this expression tree represents a constant expression.
	 *
	 * @return	Whether or not this expression tree represents a constant expression.
	 */
    @Override
    boolean isConstantExpression()
	{
		return castOperand.isConstantExpression();
	}

	/** @see ValueNode#constantExpression */
    @Override
    boolean constantExpression(PredicateList whereClause)
	{
		return castOperand.constantExpression(whereClause);
	}

	/**
	 * Return an Object representing the bind time value of this
	 * expression tree.  If the expression tree does not evaluate to
	 * a constant at bind time then we return null.
	 * This is useful for bind time resolution of VTIs.
	 * RESOLVE: What do we do for primitives?
	 *
	 * @return	An Object representing the bind time value of this expression tree.
	 *			(null if not a bind time constant.)
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
	Object getConstantValueAsObject()
		throws StandardException
	{
		Object sourceObject = castOperand.getConstantValueAsObject();

		// RESOLVE - need to figure out how to handle casts
		if (sourceObject == null)
		{
			return null;
		}

		// Simple if source and destination are of same type
		if (sourceCTI.getCorrespondingJavaTypeName().equals(
                getTypeId().getCorrespondingJavaTypeName()))
		{
			return sourceObject;
		}

		// RESOLVE - simply return null until we can figure out how to 
		// do the cast
		return null;
	}

	/**
	 * Do code generation for this unary operator.
	 *
	 * @param acb	The ExpressionClassBuilder for the class we're generating
	 * @param mb	The method the code to place the code
	 *
	 * @exception StandardException		Thrown on error
	 */
    @Override
    void generateExpression(ExpressionClassBuilder acb, MethodBuilder mb)
									throws StandardException
	{
		castOperand.generateExpression(acb, mb);

		/* No need to generate code for null constants */
		if (castOperand instanceof UntypedNullConstantNode)
		{
			return;
		}
		/* HACK ALERT. When casting a parameter, there
		 * is not sourceCTI.  Code generation requires one,
		 * so we simply set it to be the same as the
		 * destCTI.  The user can still pass whatever
		 * type they'd like in as a parameter.
		 * They'll get an exception, as expected, if the
		 * conversion cannot be performed.
		 */
		else if (castOperand.requiresTypeFromContext())
		{
			sourceCTI = getTypeId();
		}
	
		genDataValueConversion(acb, mb);
	}

	private void genDataValueConversion(ExpressionClassBuilder acb,
											  MethodBuilder mb)
			throws StandardException
	{
		MethodBuilder	acbConstructor = acb.getConstructor();

		String resultTypeName = getTypeCompiler().interfaceName();

		/* field = method call */
		/* Allocate an object for re-use to hold the result of the operator */
		LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, resultTypeName);

		/*
		** Store the result of the method call in the field, so we can re-use
		** the object.
		*/

		acb.generateNull(acbConstructor, getTypeCompiler(getTypeId()), 
                getTypeServices().getCollationType());
		acbConstructor.setField(field);


		/*
			For most types generate

			targetDVD.setValue(sourceDVD);
			
			For source or destination java types generate
			
			Object o = sourceDVD.getObject();
			targetDVD.setObjectForCast(o, o instanceof dest java type, dest java type);

			// optional for variable length types
			targetDVD.setWidth();
		*/

		if (!sourceCTI.userType() && !getTypeId().userType()) {
			mb.getField(field); // targetDVD reference for the setValue method call
			mb.swap();
			mb.upCast(ClassName.DataValueDescriptor);
			mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.DataValueDescriptor,
					"setValue", "void", 1);
		}
		else
		{
			/* 
			** generate: expr.getObject()
			*/
			mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.DataValueDescriptor,
					"getObject", "java.lang.Object", 0);

			//castExpr

			mb.getField(field); // instance for the setValue/setObjectForCast method call
			mb.swap(); // push it before the value

			/*
			** We are casting a java type, generate:
			**
			**		DataValueDescriptor.setObjectForCast(java.lang.Object castExpr, boolean instanceOfExpr, destinationClassName)
			** where instanceOfExpr is "source instanceof destinationClass".
			**
			*/
			String destinationType = getTypeId().getCorrespondingJavaTypeName();

			// at this point method instance and cast result are on the stack
			// we duplicate the cast value in order to perform the instanceof check
			mb.dup();
			mb.isInstanceOf(destinationType);
			mb.push(destinationType);
			mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.DataValueDescriptor,
					"setObjectForCast", "void", 3);

		}

		mb.getField(field);

		/* 
		** If we are casting to a variable length datatype, we
		** have to make sure we have set it to the correct
		** length.
		*/
		if (getTypeId().variableLength()) 
		{
			boolean isNumber = getTypeId().isNumericTypeId();
			
			// to leave the DataValueDescriptor value on the stack, since setWidth is void
			mb.dup();

			/* setWidth() is on VSDV - upcast since
			 * decimal implements subinterface
			 * of VSDV.
			 */
			
			mb.push(isNumber ? getTypeServices().getPrecision() : getTypeServices().getMaximumWidth());
			mb.push(getTypeServices().getScale());
            mb.push(!sourceCTI.variableLength() ||
                    isNumber ||
                    assignmentSemantics);
			mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.VariableSizeDataValue,
					"setWidth", "void", 3);

		}
	}

	/**
	 * Accept the visitor for all visitable children of this node.
	 * 
	 * @param v the visitor
	 *
	 * @exception StandardException on error
	 */
    @Override
	void acceptChildren(Visitor v)
		throws StandardException
	{
		super.acceptChildren(v);

		if (castOperand != null)
		{
			castOperand = (ValueNode)castOperand.accept(v);
		}

        if (udtTargetName != null)
        {
            udtTargetName = (TableName) udtTargetName.accept(v);
        }
	}

	/** This method gets called by the parser to indiciate that this CAST node 
	 * has been generated by the parser. This means that we should use the 
	 * collation info of the current compilation schmea for this node's 
	 * collation setting. If this method does not get called, then it means
	 * that this CAST node has been an internally generated node and we should
	 * not touch the collation of this CAST node because it has been already 
	 * set correctly by the class that generated this CAST node. 
	 */
	void setForExternallyGeneratedCASTnode()
	{
		externallyGeneratedCastNode = true;
	}

	/** set this to be a dataTypeScalarFunction
	 * 
	 * @param b true to use function conversion rules
	 */
	void setForDataTypeFunction(boolean b)
	{
		forDataTypeFunction = b;
	}

    /**
     * Set assignmentSemantics to true. Used by method calls for casting actual
     * arguments
     */
    void setAssignmentSemantics()
    {
        assignmentSemantics = true;
    }

    
	/**
	 * {@inheritDoc}
	 * @throws StandardException 
	 */
    boolean isEquivalent(ValueNode o) throws StandardException
	{
        if (isSameNodeKind(o)) {
			CastNode other = (CastNode)o;
			return getTypeServices().equals(other.getTypeServices())
				&& castOperand.isEquivalent(other.castOperand);
		}

		return false;
	}

    /**
     * Set the target type name if this is a cast to a UDT.
     * @param name the name of the target type
     */
    void setTargetUDTName(TableName name) {
        udtTargetName = name;
    }
}
