/*

   Derby - Class org.apache.derby.iapi.types.DataType

   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.iapi.types;

import org.apache.derby.shared.common.reference.SQLState;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.iapi.services.io.ArrayInputStream;
import org.apache.derby.shared.common.sanity.SanityManager;

import java.io.InputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.ResultSet;

import java.util.Calendar;

/**
 *
 * DataType is the superclass for all data types. 
 * It provides common behavior
 * for datavalue descriptors -- it throws
 * exceptions for all of the get* and setvalue(*)  methods of
 * DataValueDescriptor; the subtypes need only
 * override the one for the type they represent
 * and all types it can also be returned as,
 * and the methods dealing with nulls.
 *
 * Since all types satisfy getString 
 * DataType does not define that
 * interfaces of DataValueDescriptor.
 *
 * DataType is a little glue for columns to hold
 * values with.
 *
 */
public abstract class DataType
    implements DataValueDescriptor, Comparable
{
	/*
	 * DataValueDescriptor Interface
	 */

	/**
	 * Gets the value in the data value descriptor as a boolean.
	 * Throws an exception if the data value is not receivable as a boolean.
	 *
	 * @return	The data value as a boolean.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public boolean	getBoolean() throws StandardException
	{
		throw dataTypeConversion("boolean");
	}

	/**
	 * Gets the value in the data value descriptor as a byte.
	 * Throws an exception if the data value is not receivable as a byte.
	 *
	 * @return	The data value as a byte.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public byte	getByte() throws StandardException
	{
		throw dataTypeConversion("byte");
	}

	/**
	 * Gets the value in the data value descriptor as a short.
	 * Throws an exception if the data value is not receivable as a short.
	 *
	 * @return	The data value as a short.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public short	getShort() throws StandardException
	{
		throw dataTypeConversion("short");
	}

	/**
	 * Gets the value in the data value descriptor as a int.
	 * Throws an exception if the data value is not receivable as a int.
	 *
	 * @return	The data value as a int.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public int	getInt() throws StandardException
	{
		throw dataTypeConversion("int");
	}

	/**
	 * Gets the value in the data value descriptor as a long.
	 * Throws an exception if the data value is not receivable as a long.
	 *
	 * @return	The data value as a long.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public long	getLong() throws StandardException
	{
		throw dataTypeConversion("long");
	}

	/**
	 * Gets the value in the data value descriptor as a float.
	 * Throws an exception if the data value is not receivable as a float.
	 *
	 * @return	The data value as a float.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public float	getFloat() throws StandardException
	{
		throw dataTypeConversion("float");
	}

	/**
	 * Gets the value in the data value descriptor as a double.
	 * Throws an exception if the data value is not receivable as a double.
	 *
	 * @return	The data value as a double.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public double	getDouble() throws StandardException
	{
		throw dataTypeConversion("double");
	}

	public int typeToBigDecimal() throws StandardException
	{
		throw dataTypeConversion("java.math.BigDecimal");
	}
	/**
	 * Gets the value in the data value descriptor as a byte[].
	 * Throws an exception if the data value is not receivable as a Binary or Varbinary.
	 *
	 * @return	The Binary value as a byte[].
	 *
	 * @exception StandardException		Thrown on error
	 */
	public byte[]	getBytes() throws StandardException
	{
		throw dataTypeConversion("byte[]");
	}

	/**
	 * Gets the value in the data value descriptor as a java.sql.Date.
	 * Throws an exception if the data value is not receivable as a Date.
     *	@param cal calendar for object creation
	 * @return	The data value as a java.sql.Date.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public Date	getDate( Calendar cal) throws StandardException
	{
		throw dataTypeConversion("java.sql.Date");
	}

	/**
	 * Gets the value in the data value descriptor as a java.sql.Time.
	 * Throws an exception if the data value is not receivable as a Time.
     *	@param cal calendar for object creation
	 * @return	The data value as a java.sql.Time.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public Time	getTime( Calendar cal) throws StandardException
	{
		throw dataTypeConversion("java.sql.Time");
	}

	/**
	 * Gets the value in the data value descriptor as a java.sql.Timestamp.
	 * Throws an exception if the data value is not receivable as a Timestamp.
     *	@param cal calendar for object creation
	 * @return	The data value as a java.sql.Timestamp.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public Timestamp	getTimestamp( Calendar cal) throws StandardException
	{
		throw dataTypeConversion("java.sql.Timestamp");
	}

	/**
	 * Gets the value in the data stream descriptor as an InputStream.
	 * Throws an exception if the data value is not receivable as a stream.
	 *
	 * @return	The data value as an InputStream.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public InputStream	getStream() throws StandardException
	{
		throw dataTypeConversion( "InputStream" );
	}

    /**
     * Tells that the value isn't represented as a stream, which is true for
     * most Derby data types.
     * <p>
     * This method will be overridden by types able to use a stream as the
     * source.
     *
     * @return {@code false}
     */
    public boolean hasStream() {
        return false;
    }

    /**
     * Gets the value in the data stream descriptor as a trace string.
     * This default implementation simply forwards the call to
     * <code>getString</code>.
     *
     * @return The data value in a representation suitable for tracing.
     * @throws StandardException if getting the data value fails.
     * @see DataValueDescriptor#getString
     */
    public String getTraceString() throws StandardException {
        return getString();  
    }

    /**
     * Recycle this DataType object.
     *
     * @return this object with value set to null
     */
    public DataValueDescriptor recycle() {
        restoreToNull();
        return this;
    }

    /**
     * Read the DataValueDescriptor from the stream. The default implementation
     * calls {@code readExternal()}, which accesses the {@code ArrayInputStream}
     * as a generic stream. If sub-classes can implement it more efficiently
     * by accessing the array, they should override this method.
     *
     * @see DataValueDescriptor#readExternalFromArray
     * @see java.io.Externalizable#readExternal
     */
    public void readExternalFromArray(ArrayInputStream in)
            throws IOException, ClassNotFoundException {
        readExternal(in);
    }

	/*
	 * Column interface
	 */
	
	/**
	 * The is null operator as called from the language module, as opposed to
	 * the storage module.
	 *
	 *
	 * @return	A SQL boolean value telling whether the operand is null
	 *
	 */

	public final BooleanDataValue isNullOp()
	{
		return SQLBoolean.truthValue(isNull());
	}

	/**
	 * The is not null operator as called from the language module, as opposed to
	 * the storage module.
	 *
	 *
	 * @return	A SQL boolean value telling whether the operand is not null
	 *
	 */

	public final BooleanDataValue isNotNull()
	{
		return SQLBoolean.truthValue(!isNull());
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Time value to set this DataValueDescriptor to
	 */
	public void setValue(Time theValue) throws StandardException
	{
        setValue( theValue, (Calendar) null);
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Time value to set this DataValueDescriptor to
     * @param cal The time zone from the calendar is used to construct the database time value
	 */
	public void setValue(Time theValue, Calendar cal) throws StandardException
	{
		throwLangSetMismatch("java.sql.Time");
	}
	
	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Timestamp value to set this DataValueDescriptor to
	 */
	public void setValue(Timestamp theValue) throws StandardException
	{
        setValue( theValue, (Calendar) null);
	}
	
	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Timestamp value to set this DataValueDescriptor to
     * @param cal The time zone from the calendar is used to construct the database timestamp value
	 */
	public void setValue(Timestamp theValue, Calendar cal) throws StandardException
	{
		throwLangSetMismatch("java.sql.Timestamp");
	}
	
	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Date value to set this DataValueDescriptor to
	 */
	public void setValue(Date theValue) throws StandardException
	{
        setValue( theValue, (Calendar) null);
	}
	
	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Date value to set this DataValueDescriptor to
     * @param cal The time zone from the calendar is used to construct the database date value
	 */
	public void setValue(Date theValue, Calendar cal) throws StandardException
	{
		throwLangSetMismatch("java.sql.Date");
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Object value to set this DataValueDescriptor to
	 */
	public void setValue(Object theValue) throws StandardException
	{
		throwLangSetMismatch("java.lang.Object");
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The BigDecimal value to set this DataValueDescriptor to
	 */
	public void setValue(String theValue) throws StandardException
	{
		throwLangSetMismatch("java.lang.String");
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Blob value to set this DataValueDescriptor to
	 */
	public void setValue(Blob theValue) throws StandardException
	{
		throwLangSetMismatch("java.sql.Blob");
	}
 
	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The Clob value to set this DataValueDescriptor to
	 */
	public void setValue(Clob theValue) throws StandardException
	{
		throwLangSetMismatch("java.sql.Clob");
	}


    /**
	 * Set the value of this DataValueDescriptor to the given int value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(int theValue) throws StandardException
	{
		throwLangSetMismatch("int");
	}

	/**
	 * Set the value of this DataValueDescriptor to the given double value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(double theValue) throws StandardException
	{
		throwLangSetMismatch("double");
	}

	/**
	 * Set the value of this DataValueDescriptor to the given float value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(float theValue) throws StandardException
	{
		throwLangSetMismatch("float");
	}
 
	/**
	 * Set the value of this DataValueDescriptor to the given short value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(short theValue) throws StandardException
	{
		throwLangSetMismatch("short");
	}
	/**
	 * Set the value of this DataValueDescriptor to the given long value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(long theValue) throws StandardException
	{
		throwLangSetMismatch("long");
	}

	/**
	 * Set the value of this DataValueDescriptor to the given byte value
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The value to set this DataValueDescriptor to
	 *
	 * @exception StandardException		Thrown on error
	 */
	public void setValue(byte theValue) throws StandardException
	{
		throwLangSetMismatch("byte");
	}

	/**
	 * Set the value.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	Contains the boolean value to set this to
	 *
	 */
	public void setValue(boolean theValue) throws StandardException
	{
		throwLangSetMismatch("boolean");
	}

	/**
	 * Set the value of this DataValueDescriptor.
	 * At DataType level just throws an error lower classes will override
	 *
	 * @param theValue	The byte value to set this DataValueDescriptor to
	 *
	 */
	public void setValue(byte[] theValue) throws StandardException
	{
		throwLangSetMismatch("byte[]");
	}

	/**
		Only to be called when the application sets a value using BigDecimal
	*/
	public void setBigDecimal(BigDecimal bigDecimal) throws StandardException
	{
		throwLangSetMismatch("java.math.BigDecimal");
	}


	public final void setValue(DataValueDescriptor dvd) throws StandardException {

		if (dvd.isNull())
		{
			setToNull();
			return;
		}

		try {
			setFrom(dvd);
		} catch (StandardException se) {
			String msgId = se.getMessageId();

			if (SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE.equals(msgId))
				throw outOfRange();

			if (SQLState.LANG_FORMAT_EXCEPTION.equals(msgId))
				throw invalidFormat();

			throw se;

		}
	}

	/**
	 * Set the value of this DataValueDescriptor based on the value
	 * of the specified DataValueDescriptor.
	 *
	 * @param dvd	The DataValueDescriptor that holds the value to
	 *  which we want to set this DataValueDescriptor's value.
	 *
	 */
	protected void setFrom(DataValueDescriptor dvd) throws StandardException
	{
		throw StandardException.newException(SQLState.NOT_IMPLEMENTED);
	}

	/**
	 * @see DataValueDescriptor#setToNull
	 */
	 public void setToNull()
	 {
	 	restoreToNull();
	 }

	/**
	 * @see DataValueDescriptor#setObjectForCast
	 * 
	 * @exception StandardException
	 *                thrown on failure
	 * 
	 */
	public void setObjectForCast(Object theValue, boolean instanceOfResultType,
			String resultTypeClassName) throws StandardException {
		
		if (theValue == null)
		{
			setToNull();
			return;
		}
			
		/*
		 * Is the object of the right type? (only do the check if value is
		 * non-null
		 */
		if (!instanceOfResultType) {
				throw StandardException.newException(
						SQLState.LANG_DATA_TYPE_SET_MISMATCH,
						theValue.getClass().getName(), getTypeName(resultTypeClassName));
		}

		setObject(theValue);
	}
		
	/**
	 * Set the value from an non-null object. Usually overridden.
	 * This implementation throws an exception.
	 * The object will have been correctly typed from the call to setObjectForCast.
	 */
	void setObject(Object theValue)
		throws StandardException
	{
		genericSetObject(theValue);
	}
	
	/**
	 * Get the type name of this value, possibly overriding
	 * with the passed in class name (for user/java types).
	 * @param className
	 */
	String getTypeName(String className)
	{
		return getTypeName();
	}


	/**
	 * Gets the value in the data value descriptor as a int.
	 * Throws an exception if the data value is not receivable as a int.
	 *
	 * @return	The data value as a int.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public Object	getObject() throws StandardException
	{
		throw dataTypeConversion("java.lang.Object");
	}


	void genericSetObject(Object theValue) throws StandardException {

		throwLangSetMismatch(theValue);
	}

    /**
     * Default implementation of shallow cloning, which forwards to the deep
     * clone method.
     * <p>
     * For many of the data types, a shallow clone will be the same as a deep
     * clone. The data types requiring special handling of shallow clones have
     * to override this method (for instance types whose value can be
     * represented as a stream).
     *
     * @return A shallow clone.
     */
    public DataValueDescriptor cloneHolder() {
		return cloneValue(false);
	}

	public void throwLangSetMismatch(Object value) throws StandardException {
		throwLangSetMismatch(value.getClass().getName());
	}

	void throwLangSetMismatch(String argTypeName) throws StandardException
	{
		throw StandardException.newException(SQLState.LANG_DATA_TYPE_SET_MISMATCH, 
									   argTypeName, this.getTypeName());
		
	}

	public void setInto(PreparedStatement ps, int position) throws SQLException, StandardException {

		ps.setObject(position, getObject());
	}

	/**
		Set this value into a ResultSet for a subsequent ResultSet.insertRow
		or ResultSet.updateRow. This method will only be called for non-null values.

		@exception SQLException thrown by the ResultSet object
		@exception StandardException thrown by me accessing my value.
	*/
	public void setInto(ResultSet rs, int position) throws SQLException, StandardException {
		rs.updateObject(position, getObject());
	}


	/**
	 * Default normalization method. No information needed from DataTypeDescriptor.
	 *
	 * @param desiredType	The type to normalize the source column to
	 * @param source		The value to normalize
	 *
	 *
	 * @exception StandardException				Thrown normalization error.
	 */

	public void normalize(
				DataTypeDescriptor desiredType,
				DataValueDescriptor source)
					throws StandardException
	{
		((DataValueDescriptor) this).setValue(source);
	}
	/**
	 * Each built-in type in JSQL has a precedence.  This precedence determines
	 * how to do type promotion when using binary operators.  For example, float
	 * has a higher precedence than int, so when adding an int to a float, the
	 * result type is float.
	 *
	 * The precedence for some types is arbitrary.  For example, it doesn't
	 * matter what the precedence of the boolean type is, since it can't be
	 * mixed with other types.  But the precedence for the number types is
	 * critical.  The SQL standard requires that exact numeric types be
	 * promoted to approximate numeric when one operator uses both.  Also,
	 * the precedence is arranged so that one will not lose precision when
	 * promoting a type.
	 *
	 * @return		The precedence of this type.
	 */
	public int					typePrecedence() {
		return -1;
	}

	/**
	 * The = operator as called from the language module, as opposed to
	 * the storage module. This default implementations uses compare().
	 *
	 * @param left			The value on the left side of the =
	 * @param right			The value on the right side of the =
	 *
	 * @return	A SQL boolean value telling whether the two parameters are equal
	 *
	 * @exception StandardException		Thrown on error
	 */

	public BooleanDataValue equals(DataValueDescriptor left,
							DataValueDescriptor right)
			throws StandardException
	{
		return SQLBoolean.truthValue(left,
									 right,
									 left.compare(right) == 0);
	}

	/**
	 * The &lt;&gt; operator as called from the language module, as opposed to
	 * the storage module. This default implementations uses compare().
	 *
	 * @param left			The value on the left side of the &lt;&gt;
	 * @param right			The value on the right side of the &lt;&gt;
	 *
	 * @return	A SQL boolean value telling whether the two parameters
	 *			are not equal
	 *
	 * @exception StandardException		Thrown on error
	 */

	public BooleanDataValue notEquals(DataValueDescriptor left,
							DataValueDescriptor right)
			throws StandardException
	{
		return SQLBoolean.truthValue(left,
									 right,
									 left.compare(right) != 0);
	}
	/**
	* The &lt; operator as called from the language module, as opposed to
	* the storage module.
	*
	* @param left   The value on the left side of the &lt;
	* @param right   The value on the right side of the &lt;
	*
	* @return A SQL boolean value telling whether the first operand is less
	*   than the second operand
	*
	* @exception StandardException  Thrown on error
	*/

	public BooleanDataValue lessThan(DataValueDescriptor left,
	   DataValueDescriptor right)
	throws StandardException
	{
		return SQLBoolean.truthValue(left,
		  right,
		  left.compare(right) < 0);
	}
	/**
	 * The &gt; operator as called from the language module, as opposed to
	 * the storage module. This default implementations uses compare().
	 *
	 * @param left			The value on the left side of the &gt;
	 * @param right			The value on the right side of the &gt;
	 *
	 * @return	A SQL boolean value telling whether the first operand is greater
	 *			than the second operand
	 *
	 * @exception StandardException		Thrown on error
	 */

	public BooleanDataValue greaterThan(DataValueDescriptor left,
							DataValueDescriptor right)
			throws StandardException
	{
		return SQLBoolean.truthValue(left,
									 right,
									 left.compare(right) > 0);
	}

	/**
	 * The &lt;= operator as called from the language module, as opposed to
	 * the storage module. This default implementations uses compare().
	 *
	 * @param left			The value on the left side of the &lt;=
	 * @param right			The value on the right side of the &lt;=
	 *
	 * @return	A SQL boolean value telling whether the first operand is less
	 *			than or equal to the second operand
	 *
	 * @exception StandardException		Thrown on error
	 */

	public BooleanDataValue lessOrEquals(DataValueDescriptor left,
							DataValueDescriptor right)
			throws StandardException
	{
		return SQLBoolean.truthValue(left,
									 right,
									 left.compare(right) <= 0);
	}

	/**
	 * The &gt;= operator as called from the language module, as opposed to
	 * the storage module. This default implementation uses compare().
	 *
	 * @param left			The value on the left side of the &gt;=
	 * @param right			The value on the right side of the &gt;=
	 *
	 * @return	A SQL boolean value telling whether the first operand is greater
	 *			than or equal to the second operand
	 *
	 * @exception StandardException		Thrown on error
	 */

	public BooleanDataValue greaterOrEquals(DataValueDescriptor left,
							DataValueDescriptor right)
			throws StandardException
	{
		return SQLBoolean.truthValue(left,
									 right,
									 left.compare(right) >= 0);
	}
	public boolean compare(int op,
						   DataValueDescriptor other,
						   boolean orderedNulls,
						   boolean unknownRV)
		throws StandardException
	{
		/* Use compare method from dominant type, flipping the operator
		 * to reflect flipping of sides.
		 */
		if (typePrecedence() < other.typePrecedence())
		{
			return other.compare(flip(op), this, orderedNulls, unknownRV);
		}

		int result = compare(other);

		switch(op)
		{
		case ORDER_OP_LESSTHAN:
			return (result < 0);   // this <  other
		case ORDER_OP_EQUALS:
			return (result == 0);  // this == other
		case ORDER_OP_LESSOREQUALS:
			return (result <= 0);  // this <= other
		// flipped operators
		case ORDER_OP_GREATERTHAN:
			return (result > 0);   // this > other
		case ORDER_OP_GREATEROREQUALS:
			return (result >= 0);  // this >= other
		default:
			if (SanityManager.DEBUG)
				SanityManager.THROWASSERT("Invalid Operator");
			return false;
		}
	}

	/**
	 * Compare this Orderable with another, with configurable null ordering.
	 * The caller gets to determine how nulls
	 * should be treated - they can either be ordered values or unknown
	 * values. The caller also gets to decide, if they are ordered,
         * whether they should be lower than non-NULL values, or higher
	 *
	 * @param op	Orderable.ORDER_OP_EQUALS means do an = comparison.
	 *				Orderable.ORDER_OP_LESSTHAN means compare this &lt; other.
	 *				Orderable.ORDER_OP_LESSOREQUALS means compare this &lt;= other.
	 * @param other	The DataValueDescriptor to compare this one to.
	 * @param orderedNulls	True means to treat nulls as ordered values,
	 *						that is, treat SQL null as equal to null, and either greater or less
	 *						than all other values.
	 *						False means to treat nulls as unknown values,
	 *						that is, the result of any comparison with a null
	 *						is the UNKNOWN truth value.
         * @param nullsOrderedLow       True means NULL less than non-NULL,
         *                              false means NULL greater than non-NULL.
         *                              Only relevant if orderedNulls is true.
	 * @param unknownRV		The return value to use if the result of the
	 *						comparison is the UNKNOWN truth value.  In other
	 *						words, if orderedNulls is false, and a null is
	 *						involved in the comparison, return unknownRV.
	 *						This parameter is not used orderedNulls is true.
	 *
	 * @return	true if the comparison is true (duh!)
	 *
	 * @exception StandardException		Thrown on error
	 */
	public boolean compare(int op,
						   DataValueDescriptor other,
						   boolean orderedNulls,
						   boolean nullsOrderedLow,
						   boolean unknownRV)
		throws StandardException
	{
		/* Use compare method from dominant type, flipping the operator
		 * to reflect flipping of sides.
		 */
		if (typePrecedence() < other.typePrecedence())
		{
			return other.compare(flip(op), this, orderedNulls,
                                nullsOrderedLow, unknownRV);
		}

		int result = compare(other, nullsOrderedLow);

		switch(op)
		{
		case ORDER_OP_LESSTHAN:
			return (result < 0);   // this <  other
		case ORDER_OP_EQUALS:
			return (result == 0);  // this == other
		case ORDER_OP_LESSOREQUALS:
			return (result <= 0);  // this <= other
		// flipped operators
		case ORDER_OP_GREATERTHAN:
			return (result > 0);   // this > other
		case ORDER_OP_GREATEROREQUALS:
			return (result >= 0);  // this >= other
		default:
			if (SanityManager.DEBUG)
				SanityManager.THROWASSERT("Invalid Operator");
			return false;
		}
	}

	/**
	 * Compare this Orderable with another, with configurable null ordering.
	 * This method treats nulls as ordered values, but allows the caller
         * to specify whether they should be lower than all non-NULL values,
         * or higher than all non-NULL values.
	 *
	 * @param other		The Orderable to compare this one to.
         % @param nullsOrderedLow True if null should be lower than non-NULL
	 *
	 * @return  &lt;0 - this Orderable is less than other.
	 * 			 0 - this Orderable equals other.
	 *			&gt;0 - this Orderable is greater than other.
     *
     *			The code should not explicitly look for -1, or 1.
	 *
	 * @exception StandardException		Thrown on error
	 */
	public int compare(DataValueDescriptor other, boolean nullsOrderedLow)
            throws StandardException
        {
            if (this.isNull() || other.isNull())
            {
                if (!isNull())
                    return nullsOrderedLow ? 1 : -1;
                if (!other.isNull())
                    return nullsOrderedLow ? -1 : 1;
                return 0; // both null
            }
            return compare(other);
        } 

	/**
	 * Wrapper method for the "compare(DataValueDescriptor)" method of
	 * this class.  Allows sorting of an array of DataValueDescriptors
	 * using the JVMs own sorting algorithm.  Currently used for
	 * execution-time sorting of IN-list values to allow proper handling
	 * (i.e. elimination) of duplicates.
	 *
	 * @see java.lang.Comparable#compareTo
	 */
	public int compareTo(Object otherDVD)
	{
		DataValueDescriptor other = (DataValueDescriptor)otherDVD;
		try {

			// Use compare method from the dominant type.
			if (typePrecedence() < other.typePrecedence())
				return (-1 * other.compare(this));

			return compare(other);

		} catch (StandardException se) {

			if (SanityManager.DEBUG)
			{
				SanityManager.THROWASSERT("Encountered error while " +
					"trying to compare two DataValueDescriptors: " +
					se.getMessage());
			}

			/* In case of an error in insane mode, just treat the
			 * values as "equal".
			 */
			return 0;
		}
	}

	/**
	 * Flip the operator used in a comparison (&lt; -&gt; &gt;).
	 * This is useful when flipping a comparison due to
	 * type precedence.
	 * 
	 * @param operator	The operator to flip.
	 * 
	 * @return The flipped operator.
	 */
	protected static int flip(int operator)
	{
		switch (operator)
		{
			case Orderable.ORDER_OP_LESSTHAN:
				// < -> > 
				return Orderable.ORDER_OP_GREATERTHAN;
			case Orderable.ORDER_OP_LESSOREQUALS:
				// <= -> >= 
				return Orderable.ORDER_OP_GREATEROREQUALS;
			case Orderable.ORDER_OP_EQUALS:
				// = -> = 
				return Orderable.ORDER_OP_EQUALS;
			default:
				// These operators only appear due to flipping.
				// They should never be flipped themselves.
				if (SanityManager.DEBUG)
				{
					SanityManager.THROWASSERT(
						"Attempting to flip an operator that is not " +
						"expected to be flipped.");
				}
				return operator;
		}
	}

	/*
	 * DataValueDescriptor interface
	 */

	/**
	 * @see DataValueDescriptor#coalesce
	 * @exception StandardException		Thrown on error
	 */
	public DataValueDescriptor coalesce(DataValueDescriptor[] argumentsList, DataValueDescriptor returnValue)
						throws StandardException
	{
		// arguments list should have at least 2 arguments
		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(argumentsList != null,
				"argumentsList expected to be non-null");
			SanityManager.ASSERT(argumentsList.length > 1,
				"argumentsList.length expected to be > 1");
		}

		/* Walk the arguments list until we find a non-null value. Otherwise we will return null
		 */
		int index;
		for (index = 0; index < argumentsList.length; index++)
		{
			if (!(argumentsList[index].isNull()))
			{
				returnValue.setValue(argumentsList[index]);
				return returnValue;
			}
		}

		returnValue.setToNull();
		return returnValue;

	}

	/**
	 * @see DataValueDescriptor#in
	 * @exception StandardException		Thrown on error
	 */
	public BooleanDataValue in(DataValueDescriptor left,
							   DataValueDescriptor[] inList,
							   boolean orderedList) 
						throws StandardException
	{
		BooleanDataValue retval = null;

		// in list should be non-empty
		if (SanityManager.DEBUG)
		{
			SanityManager.ASSERT(inList != null,
				"inList expected to be non-null");
			SanityManager.ASSERT(inList.length > 0,
				"inList.length expected to be > 0");
		}

		// if left is null then just return false
		if (left.isNull())
		{
			return SQLBoolean.truthValue(left,
									 inList[0],
									 false);
		}

		int start = 0;
		int finish = inList.length;

		/* Do a binary search if the list is ordered until the 
		 * range of values to search is 3 or less.
		 *
		 * NOTE: We may have sorted the IN-lst values at compile time using
		 * a specific (dominant) type, but we did *not* actually cast the
		 * values to that type.  So it's possible that different IN-list
		 * values have different precedences (verses each other and also
		 * verses the type of the left operand) when we get here.  Thus
		 * when we do any comparisons here we have to make sure we always
		 * compare using the dominant type of the two values being compared.
		 * Otherwise we can end up with wrong results when doing the binary
		 * search (ex. as caused by incorrect truncation).  DERBY-2256.
         *
         * DERBY-6017: Actually, it's not good enough to compare using the
         * dominant type of the two values being compared. It has to be the
         * dominant type of *all* the values in the left operand and the right
         * operand. Therefore, InListOperatorNode.preprocess() inserts a
         * cast if necessary to ensure that either the left side or the right
         * side of each comparison is of the overall dominating type.
		 */
		int leftPrecedence = left.typePrecedence();
		DataValueDescriptor comparator = null;
		if (orderedList)
		{
			while (finish - start > 2)
			{
				int mid = ((finish - start) / 2) + start;
				comparator =
					(leftPrecedence < inList[mid].typePrecedence())
						? inList[mid]
						: left;

				// Search left
				retval = comparator.equals(left, inList[mid]);
				if (retval.equals(true))
				{
					return retval;
				}
				BooleanDataValue goLeft =
					comparator.greaterThan(inList[mid], left);
				if (goLeft.equals(true))
				{
					// search left
					finish = mid;
				}
				else
				{
					// search right
					start = mid;
				}
			}
		}

		/* Walk the in list comparing the values.  Return as soon as we
		 * find a match.  If the list is ordered, return as soon as the left
		 * value is greater than an element in the in list.
		 *
		 * Note: for the same reasons outlined above we must be sure to always
		 * do the comparisons using the dominant type of the two values being
		 * compared.
		 */
		for (int index = start; index < finish; index++)
		{
			comparator =
				(leftPrecedence < inList[index].typePrecedence())
					? inList[index]
					: left;

			retval = comparator.equals(left, inList[index]);
			if (retval.equals(true))
			{
				break;
			}

			// Can we stop searching?
			if (orderedList)
			{
				BooleanDataValue stop =
					comparator.greaterThan(inList[index], left);
				if (stop.equals(true))
				{
					break;
				}
			}
		}

		return retval;
	}

	/*
	 * equals
	 */
	public boolean equals(Object other)
	{
		if (! (other instanceof DataValueDescriptor))
		{
			return false;
		}

		try
		{
			return compare(ORDER_OP_EQUALS, (DataValueDescriptor) other, true, false);
		}
		catch (StandardException se)
		{
			return false;
		}
	}

	public void setValue(InputStream theStream, int valueLength) throws StandardException
	{
		throwLangSetMismatch("java.io.InputStream");
	}

	/**
		Check the value to seem if it conforms to the restrictions
		imposed by DB2/JCC on host variables for this type.

		@exception StandardException Variable is too big.
	*/
	public void checkHostVariable(int declaredLength) throws StandardException
	{
	}


	/**
		Return an conversion exception from this type to another.
	*/
	protected final StandardException dataTypeConversion(String targetType) {
		return StandardException.newException(SQLState.LANG_DATA_TYPE_GET_MISMATCH, 
			targetType, this.getTypeName());

	}

	/**
		Return an out of range exception for this type.
	*/
	protected final StandardException outOfRange()
	{
		return StandardException.newException(
				SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE, getTypeName());
	}

	/**
		Return an out of range exception for this type.
	*/
	protected final StandardException invalidFormat()
	{
		return StandardException.newException(
				SQLState.LANG_FORMAT_EXCEPTION, getTypeName());
	}
}
