blob: fe2c1a3b79a5c880786e57d75eb0cfed165b5c36 [file] [log] [blame]
/*
Derby - Class org.apache.derby.iapi.types.UserType
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.catalog.TypeDescriptor;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.loader.ClassInspector;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.services.io.StoredFormatIds;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.services.cache.ClassSize;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.io.ObjectOutput;
import java.io.ObjectInput;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
/**
* This contains an instance of a user-defined type, that is, a java object.
*
*/
public class UserType extends DataType
implements UserDataValue
{
private Object value;
/*
** DataValueDescriptor interface
** (mostly implemented in DataType)
*/
private static final int BASE_MEMORY_USAGE = ClassSize.estimateBaseFromCatalog( UserType.class);
public int estimateMemoryUsage()
{
int sz = BASE_MEMORY_USAGE;
if( null != value)
{
// Probably an underestimate. Examining each field value would be expensive
// and would produce an overestimate when fields reference shared objects
sz += ClassSize.estimateAndCatalogBase( value.getClass());
}
return sz;
} // end of estimateMemoryUsage
public String getString()
{
if (! isNull())
{
return value.toString();
}
else
{
return null;
}
}
/**
* @exception StandardException thrown on failure to convert
*/
public boolean getBoolean() throws StandardException
{
if (! isNull())
if (value instanceof Boolean) return ((Boolean)value).booleanValue();
return super.getBoolean();
}
/**
* @exception StandardException thrown on failure to convert
*/
public byte getByte() throws StandardException
{
if (! isNull())
// REMIND: check for overflow and truncation
if (value instanceof Number) return ((Number)value).byteValue();
return super.getByte();
}
/**
* @exception StandardException thrown on failure to convert
*/
public short getShort() throws StandardException
{
if (! isNull())
// REMIND: check for overflow and truncation
if (value instanceof Number) return ((Number)value).shortValue();
return super.getShort();
}
/**
* @exception StandardException thrown on failure to convert
*/
public int getInt() throws StandardException
{
if (! isNull())
// REMIND: check for overflow and truncation
if (value instanceof Number) return ((Number)value).intValue();
return super.getInt();
}
/**
* @exception StandardException thrown on failure to convert
*/
public long getLong() throws StandardException
{
if (! isNull())
// REMIND: check for overflow and truncation
if (value instanceof Number) return ((Number)value).longValue();
return super.getLong();
}
/**
* @exception StandardException thrown on failure to convert
*/
public float getFloat() throws StandardException
{
if (! isNull())
// REMIND: check for overflow
if (value instanceof Number) return ((Number)value).floatValue();
return super.getFloat();
}
/**
* @exception StandardException thrown on failure to convert
*/
public double getDouble() throws StandardException
{
if (! isNull())
// REMIND: check for overflow
if (value instanceof Number) return ((Number)value).doubleValue();
return super.getDouble();
}
/**
* @exception StandardException thrown on failure to convert
*/
public byte[] getBytes() throws StandardException
{
if (! isNull())
if (value instanceof byte[]) return ((byte[])value);
return super.getBytes();
}
/**
@exception StandardException thrown on failure
*/
public Date getDate( Calendar cal) throws StandardException
{
if (! isNull())
{
if (value instanceof Date)
return ((Date)value);
else if (value instanceof Timestamp)
return (new SQLTimestamp((Timestamp)value).getDate(cal));
}
return super.getDate(cal);
}
/**
@exception StandardException thrown on failure
*/
public Time getTime( Calendar cal) throws StandardException
{
if (! isNull())
{
if (value instanceof Time)
return ((Time)value);
else if (value instanceof Timestamp)
return (new SQLTimestamp((Timestamp)value).getTime(cal));
}
return super.getTime(cal);
}
/**
@exception StandardException thrown on failure
*/
public Timestamp getTimestamp( Calendar cal) throws StandardException
{
if (! isNull())
{
if (value instanceof Timestamp)
return ((Timestamp)value);
else if (value instanceof Date)
return (new SQLDate((Date)value).getTimestamp(cal));
else if (value instanceof Time)
return (new SQLTime((Time)value).getTimestamp(cal));
}
return super.getTimestamp(cal);
}
void setObject(Object theValue)
{
setValue( theValue );
}
public Object getObject()
{
return value;
}
public int getLength()
{
return TypeDescriptor.MAXIMUM_WIDTH_UNKNOWN;
}
/* this is for DataType's error generator */
public String getTypeName()
{
return isNull() ? "JAVA_OBJECT" : ClassInspector.readableClassName(value.getClass());
}
/**
* Get the type name of this value, overriding
* with the passed in class name (for user/java types).
*/
String getTypeName(String className)
{
return className;
}
/*
* Storable interface, implies Externalizable, TypedFormat
*/
/**
Return my format identifier.
@see org.apache.derby.iapi.services.io.TypedFormat#getTypeFormatId
*/
public int getTypeFormatId() {
return StoredFormatIds.SQL_USERTYPE_ID_V3;
}
/**
@exception IOException error writing data
*/
public void writeExternal(ObjectOutput out) throws IOException {
if (SanityManager.DEBUG)
SanityManager.ASSERT(!isNull(), "writeExternal() is not supposed to be called for null values.");
out.writeObject(value);
}
/**
* @see java.io.Externalizable#readExternal
*
* @exception IOException Thrown on error reading the object
* @exception ClassNotFoundException Thrown if the class of the object
* is not found
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
/* RESOLVE: Sanity check for right class */
value = in.readObject();
}
/*
* DataValueDescriptor interface
*/
/** @see DataValueDescriptor#cloneValue */
public DataValueDescriptor cloneValue(boolean forceMaterialization)
{
// Call constructor with all of our info
return new UserType(value);
}
/**
* @see DataValueDescriptor#getNewNull
*/
public DataValueDescriptor getNewNull()
{
return new UserType();
}
/**
* @see org.apache.derby.iapi.services.io.Storable#restoreToNull
*
*/
public void restoreToNull()
{
value = null;
}
/*
* DataValueDescriptor interface
*/
/**
* @see DataValueDescriptor#setValueFromResultSet
*
* @exception SQLException Thrown on error
*/
public void setValueFromResultSet(ResultSet resultSet, int colNumber,
boolean isNullable)
throws SQLException
{
value = resultSet.getObject(colNumber);
}
/**
* Orderable interface
*
*
* @see org.apache.derby.iapi.types.Orderable
*
* @exception StandardException thrown on failure
*/
@SuppressWarnings("unchecked")
public int compare(DataValueDescriptor other)
throws StandardException
{
/* Use compare method from dominant type, negating result
* to reflect flipping of sides.
*/
if (typePrecedence() < other.typePrecedence())
{
return - (other.compare(this));
}
boolean thisNull, otherNull;
thisNull = this.isNull();
otherNull = other.isNull();
/*
* thisNull otherNull return
* T T 0 (this == other)
* F T -1 (this < other)
* T F 1 (this > other)
*/
if (thisNull || otherNull)
{
if (!thisNull) // otherNull must be true
return -1;
if (!otherNull) // thisNull must be true
return 1;
return 0;
}
/*
Neither are null compare them
*/
int comparison;
try
{
comparison = ((java.lang.Comparable<Object>) value).compareTo(other.getObject());
}
catch (ClassCastException cce)
{
throw StandardException.newException(SQLState.LANG_INVALID_COMPARE_TO,
getTypeName(),
ClassInspector.readableClassName(other.getObject().getClass()));
}
/*
** compareTo() can return any negative number if less than, and
** any positive number if greater than. Change to -1, 0, 1.
*/
if (comparison < 0)
comparison = -1;
else if (comparison > 0)
comparison = 1;
return comparison;
}
/**
@exception StandardException thrown on error
*/
public boolean compare(int op,
DataValueDescriptor other,
boolean orderedNulls,
boolean unknownRV)
throws StandardException
{
if (!orderedNulls) // nulls are unordered
{
if (this.isNull() || other.isNull())
return unknownRV;
}
/* For usertypes and equal do some special processing when
* neither value is null. (Superclass will handle comparison
* if either value is null.)
*/
if ( (op == ORDER_OP_EQUALS) &&
(! this.isNull()) && (! other.isNull()) )
{
// if this object implements java.lang.Comparable (JDK1.2)
// then we let the compareTo method handle equality
// if it doesn't then we use the equals() method
Object o = getObject();
if (!(o instanceof java.lang.Comparable))
{
return o.equals(other.getObject());
}
}
/* Do the comparison */
return super.compare(op, other, orderedNulls, unknownRV);
}
/*
** Class interface
*/
/*
** Constructors
*/
/** no-arg constructor required by Formattable */
public UserType() { }
public UserType(Object value)
{
this.value = value;
}
/**
* @see UserDataValue#setValue
*
*/
public void setValue(Object value)
{
this.value = value;
}
protected void setFrom(DataValueDescriptor theValue) throws StandardException {
setValue(theValue.getObject());
}
/**
* @see UserDataValue#setValue
*
*/
public void setBigDecimal(BigDecimal theValue)
{
// needed to allow serializable BigDecimal
setValue((Object) theValue);
}
public void setValue(String theValue)
{
if (theValue == null)
{
value = null;
}
else
{
// Higher levels must have performed type checking for us.
value = theValue;
}
}
/*
** SQL Operators
*/
/**
* The = operator as called from the language module, as opposed to
* the storage module.
*
* @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(ORDER_OP_EQUALS, right, true, false));
}
/**
* The &lt;&gt; operator as called from the language module, as opposed to
* the storage module.
*
* @param left The value on the left side of the operator
* @param right The value on the right side of the operator
*
* @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(ORDER_OP_EQUALS, right, true, false));
}
/*
** String display of value
*/
public String toString()
{
if (isNull())
{
return "NULL";
}
else
{
return value.toString();
}
}
/*
* Hash code
*/
public int hashCode()
{
if (isNull())
return 0;
return value.hashCode();
}
/** @see DataValueDescriptor#typePrecedence */
public int typePrecedence()
{
return TypeId.USER_PRECEDENCE;
}
/**
* Check if the value is null.
*
* @return Whether or not value is logically null.
*/
public final boolean isNull()
{
return (value == null);
}
}