/*
* TableRow.java
*
* Version: $Revision: 3705 $
*
* Date: $Date: 2009-04-11 18:02:24 +0100 (Sat, 11 Apr 2009) $
*
* Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.storage.rdbms;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dspace.core.ConfigurationManager;
/**
* Represents a database row.
*
* @author Peter Breton
* @version $Revision: 3705 $
*/
public class TableRow
{
/** Marker object to indicate NULLs. */
private static final Object NULL_OBJECT = new Object();
/** The name of the database table containing this row */
private String table;
/**
* A map of column names to column values. The key of the map is a String,
* the column name; the value is an Object, either an Integer, Boolean,
* Date, or String. If the value is NULL_OBJECT, then the column was NULL.
*/
private Map data = new HashMap();
private Map changed = new HashMap();
/**
* Constructor
*
* @param table
* The name of the database table containing this row.
* @param columns
* A list of column names. Each member of the List is a String.
* After construction, the list of columns is fixed; attempting
* to access a column not in the list will cause an
* IllegalArgumentException to be thrown.
*/
public TableRow(String table, List columns)
{
this.table = table;
nullColumns(columns);
resetChanged(columns);
}
/**
* Return the name of the table containing this row, or null if this row is
* not associated with a database table.
*
* @return The name of the table containing this row
*/
public String getTable()
{
return table;
}
/**
* Return true if this row contains a column with this name.
*
* @param column
* The column name (case-insensitive)
* @return True if this row contains a column with this name.
*/
public boolean hasColumn(String column)
{
return data.get(canonicalize(column)) != null;
}
/**
* Return true if this row contains this column and the value has been updated.
*
* @param column
* The column name (case-insensitive)
* @return True if this row contains a column with this name.
*/
public boolean hasColumnChanged(String column)
{
return changed.get(canonicalize(column)) == Boolean.TRUE;
}
/**
* Return true if the column is an SQL NULL.
*
* @param column
* The column name (case-insensitive)
* @return True if the column is an SQL NULL
*/
public boolean isColumnNull(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
return data.get(canonicalize(column)) == NULL_OBJECT;
}
/**
* Return the integer value of column.
*
* If the column's type is not an integer, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The integer value of the column, or -1 if the column is an SQL
* null.
*/
public int getIntColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return -1;
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
if (!(value instanceof Integer))
{
throw new IllegalArgumentException("Value for " + column
+ " is not an integer");
}
return ((Integer) value).intValue();
}
/**
* Return the long value of column.
*
* If the column's type is not an long, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The long value of the column, or -1 if the column is an SQL null.
*/
public long getLongColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return -1;
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
// If the value is an integer, it can be represented without error as a long
// So, allow the return of a long. (This is needed for Oracle support).
if ((value instanceof Integer))
{
return ((Integer) value).longValue();
}
if (!(value instanceof Long))
{
throw new IllegalArgumentException("Value for " + column
+ " is not a long");
}
return ((Long) value).longValue();
}
/**
* Added by CG for Stats
* Return the double value of column.
*
* If the column's type is not an float, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The double value of the column, or -1 if the column is an SQL null.
*/
public double getDoubleColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return -1;
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
if (!(value instanceof Double))
{
throw new IllegalArgumentException("Value for " + column
+ " is not a double");
}
return ((Double) value).doubleValue();
}
/**
* Added by CG for Stats
* Return the bigint value of column.
*
* If the column's type is not an float, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The double value of the column, or -1 if the column is an SQL null.
*/
public String getBigIntColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return "";
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
return value.toString();
}
/**
* Return the String value of column.
*
* If the column's type is not a String, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The String value of the column, or null if the column is an SQL
* null.
*/
public String getStringColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return null;
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
if (!(value instanceof String))
{
throw new IllegalArgumentException("Value is not an string");
}
return (String) value;
}
/**
* Return the boolean value of column.
*
* If the column's type is not a boolean, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return The boolean value of the column, or false if the column is an SQL
* null.
*/
public boolean getBooleanColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return false;
}
Object value = data.get(name);
// make sure that we tolerate integers or booleans
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
if ((value instanceof Boolean))
{
return ((Boolean) value).booleanValue();
}
else if ((value instanceof Integer))
{
int i = ((Integer) value).intValue();
if (i == 0)
{
return false; // 0 is false
}
return true; // nonzero is true
}
else
{
throw new IllegalArgumentException(
"Value is not a boolean or an integer");
}
}
/**
* Return the date value of column.
*
* If the column's type is not a date, or the column does not exist, an
* IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @return - The date value of the column, or null if the column is an SQL
* null.
*/
public java.util.Date getDateColumn(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String name = canonicalize(column);
if (isColumnNull(name))
{
return null;
}
Object value = data.get(name);
if (value == null)
{
throw new IllegalArgumentException("Column " + column
+ " not present");
}
if (!(value instanceof java.util.Date))
{
throw new IllegalArgumentException("Value is not a Date");
}
return (java.util.Date) value;
}
/**
* Set column to an SQL NULL.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
*/
public void setColumnNull(String column)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
setColumnNullInternal(canonicalize(column));
}
/**
* Set column to the boolean b.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param b
* The boolean value
*/
public void setColumn(String column, boolean b)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
if ("oracle".equals(ConfigurationManager.getProperty("db.name")))
{
// if oracle, use 1 or 0 for true/false
Integer value = b ? new Integer(1) : new Integer(0);
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
else
{
// default to postgres true/false
Boolean value = b ? Boolean.TRUE : Boolean.FALSE;
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
}
/**
* Set column to the String s. If s is null, the column is set to null.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param s
* The String value
*/
public void setColumn(String column, String s)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
Object value = (s == null) ? NULL_OBJECT : s;
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
/**
* Set column to the integer i.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param i
* The integer value
*/
public void setColumn(String column, int i)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
Integer value = new Integer(i);
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
/**
* Set column to the long l.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param l
* The long value
*/
public void setColumn(String column, long l)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
Long value = new Long(l);
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
/**
* Added by CG for stats
* Set column to the double d.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param l
* The double value
*/
public void setColumn(String column, double d)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
Double value = new Double(d);
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
/**
* Set column to the date d. If the date is null, the column is set to NULL
* as well.
*
* If the column does not exist, an IllegalArgumentException is thrown.
*
* @param column
* The column name (case-insensitive)
* @param d
* The date value
*/
public void setColumn(String column, java.util.Date d)
{
if (!hasColumn(column))
{
throw new IllegalArgumentException("No such column " + column);
}
String canonName = canonicalize(column);
Object value = (d == null) ? NULL_OBJECT : d;
if (!value.equals(data.get(canonName)))
{
data.put(canonName, value);
changed.put(canonName, Boolean.TRUE);
}
}
////////////////////////////////////////
// Utility methods
////////////////////////////////////////
/**
* Return a String representation of this object.
*
* @return String representaton
*/
public String toString()
{
final String NEWLINE = System.getProperty("line.separator");
StringBuffer result;
if (table==null)
{
result = new StringBuffer("no_table");
}
else
{
result = new StringBuffer(table);
}
result.append(NEWLINE);
for (Iterator iterator = data.keySet().iterator(); iterator.hasNext();)
{
String column = (String) iterator.next();
result.append("\t").append(column).append(" = ").append(
isColumnNull(column) ? "NULL" : data.get(column)).append(
NEWLINE);
}
return result.toString();
}
/**
* Return a hash code for this object.
*
* @return int hash of object
*/
public int hashCode()
{
return toString().hashCode();
}
/**
* Return true if this object equals obj, false otherwise.
*
* @param obj
* @return true if TableRow objects are equal
*/
public boolean equals(Object obj)
{
if (!(obj instanceof TableRow))
{
return false;
}
return data.equals(((TableRow) obj).data);
}
/**
* Return the canonical name for column.
*
* @param column
* The name of the column.
* @return The canonical name of the column.
*/
static String canonicalize(String column)
{
if ("oracle".equals(ConfigurationManager.getProperty("db.name")))
{
// oracle requires uppercase
return column.toUpperCase();
}
// postgres default lowercase
return column.toLowerCase();
}
/**
* Set columns to null.
*
* @param columns -
* A list of the columns to set to null. Each element of the list
* is a String.
*/
private void nullColumns(List columns)
{
for (Iterator iterator = columns.iterator(); iterator.hasNext();)
{
setColumnNullInternal((String) iterator.next());
}
}
private void resetChanged(List columns)
{
for (Iterator iterator = columns.iterator(); iterator.hasNext();)
{
changed.put(canonicalize((String) iterator.next()), Boolean.FALSE);
}
}
/**
* package private method to reset the flags of which columns have been updated
* This is used by the database manager after it has finished processing the contents
* of a resultset, so that it can update only columns that have been updated.
* Note that this method does not reset the values themselves, only the flags,
* and should not be considered safe to call from anywhere other than the DatabaseManager.
*/
void resetChanged()
{
for (Iterator iterator = changed.keySet().iterator(); iterator.hasNext();)
{
changed.put(canonicalize((String) iterator.next()), Boolean.FALSE);
}
}
/**
* Internal method to set column to null. The public method ensures that
* column actually exists.
*
* @param column
*/
private void setColumnNullInternal(String column)
{
String canonName = canonicalize(column);
if (data.get(canonName) != NULL_OBJECT)
{
data.put(canonName, NULL_OBJECT);
changed.put(canonName, Boolean.TRUE);
}
}
}