001: /*
002: * TableRow.java
003: *
004: * Version: $Revision: 2639 $
005: *
006: * Date: $Date: 2008-02-11 18:03:08 -0600 (Mon, 11 Feb 2008) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.storage.rdbms;
041:
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.List;
045: import java.util.Map;
046:
047: import org.dspace.core.ConfigurationManager;
048:
049: /**
050: * Represents a database row.
051: *
052: * @author Peter Breton
053: * @version $Revision: 2639 $
054: */
055: public class TableRow {
056: /** Marker object to indicate NULLs. */
057: private static final Object NULL_OBJECT = new Object();
058:
059: /** The name of the database table containing this row */
060: private String table;
061:
062: /**
063: * A map of column names to column values. The key of the map is a String,
064: * the column name; the value is an Object, either an Integer, Boolean,
065: * Date, or String. If the value is NULL_OBJECT, then the column was NULL.
066: */
067: private Map data = new HashMap();
068:
069: private Map changed = new HashMap();
070:
071: /**
072: * Constructor
073: *
074: * @param table
075: * The name of the database table containing this row.
076: * @param columns
077: * A list of column names. Each member of the List is a String.
078: * After construction, the list of columns is fixed; attempting
079: * to access a column not in the list will cause an
080: * IllegalArgumentException to be thrown.
081: */
082: public TableRow(String table, List columns) {
083: this .table = table;
084: nullColumns(columns);
085: resetChanged(columns);
086: }
087:
088: /**
089: * Return the name of the table containing this row, or null if this row is
090: * not associated with a database table.
091: *
092: * @return The name of the table containing this row
093: */
094: public String getTable() {
095: return table;
096: }
097:
098: /**
099: * Return true if this row contains a column with this name.
100: *
101: * @param column
102: * The column name (case-insensitive)
103: * @return True if this row contains a column with this name.
104: */
105: public boolean hasColumn(String column) {
106: return data.get(canonicalize(column)) != null;
107: }
108:
109: /**
110: * Return true if this row contains this column and the value has been updated.
111: *
112: * @param column
113: * The column name (case-insensitive)
114: * @return True if this row contains a column with this name.
115: */
116: public boolean hasColumnChanged(String column) {
117: return changed.get(canonicalize(column)) == Boolean.TRUE;
118: }
119:
120: /**
121: * Return true if the column is an SQL NULL.
122: *
123: * @param column
124: * The column name (case-insensitive)
125: * @return True if the column is an SQL NULL
126: */
127: public boolean isColumnNull(String column) {
128: if (!hasColumn(column)) {
129: throw new IllegalArgumentException("No such column "
130: + column);
131: }
132:
133: return data.get(canonicalize(column)) == NULL_OBJECT;
134: }
135:
136: /**
137: * Return the integer value of column.
138: *
139: * If the column's type is not an integer, or the column does not exist, an
140: * IllegalArgumentException is thrown.
141: *
142: * @param column
143: * The column name (case-insensitive)
144: * @return The integer value of the column, or -1 if the column is an SQL
145: * null.
146: */
147: public int getIntColumn(String column) {
148: if (!hasColumn(column)) {
149: throw new IllegalArgumentException("No such column "
150: + column);
151: }
152:
153: String name = canonicalize(column);
154:
155: if (isColumnNull(name)) {
156: return -1;
157: }
158:
159: Object value = data.get(name);
160:
161: if (value == null) {
162: throw new IllegalArgumentException("Column " + column
163: + " not present");
164: }
165:
166: if (!(value instanceof Integer)) {
167: throw new IllegalArgumentException("Value for " + column
168: + " is not an integer");
169: }
170:
171: return ((Integer) value).intValue();
172: }
173:
174: /**
175: * Return the long value of column.
176: *
177: * If the column's type is not an long, or the column does not exist, an
178: * IllegalArgumentException is thrown.
179: *
180: * @param column
181: * The column name (case-insensitive)
182: * @return The long value of the column, or -1 if the column is an SQL null.
183: */
184: public long getLongColumn(String column) {
185: if (!hasColumn(column)) {
186: throw new IllegalArgumentException("No such column "
187: + column);
188: }
189:
190: String name = canonicalize(column);
191:
192: if (isColumnNull(name)) {
193: return -1;
194: }
195:
196: Object value = data.get(name);
197:
198: if (value == null) {
199: throw new IllegalArgumentException("Column " + column
200: + " not present");
201: }
202:
203: // If the value is an integer, it can be represented without error as a long
204: // So, allow the return of a long. (This is needed for Oracle support).
205: if ((value instanceof Integer)) {
206: return ((Integer) value).longValue();
207: }
208:
209: if (!(value instanceof Long)) {
210: throw new IllegalArgumentException("Value for " + column
211: + " is not a long");
212: }
213:
214: return ((Long) value).longValue();
215: }
216:
217: /**
218: * Return the String value of column.
219: *
220: * If the column's type is not a String, or the column does not exist, an
221: * IllegalArgumentException is thrown.
222: *
223: * @param column
224: * The column name (case-insensitive)
225: * @return The String value of the column, or null if the column is an SQL
226: * null.
227: */
228: public String getStringColumn(String column) {
229: if (!hasColumn(column)) {
230: throw new IllegalArgumentException("No such column "
231: + column);
232: }
233:
234: String name = canonicalize(column);
235:
236: if (isColumnNull(name)) {
237: return null;
238: }
239:
240: Object value = data.get(name);
241:
242: if (value == null) {
243: throw new IllegalArgumentException("Column " + column
244: + " not present");
245: }
246:
247: if (!(value instanceof String)) {
248: throw new IllegalArgumentException("Value is not an string");
249: }
250:
251: return (String) value;
252: }
253:
254: /**
255: * Return the boolean value of column.
256: *
257: * If the column's type is not a boolean, or the column does not exist, an
258: * IllegalArgumentException is thrown.
259: *
260: * @param column
261: * The column name (case-insensitive)
262: * @return The boolean value of the column, or false if the column is an SQL
263: * null.
264: */
265: public boolean getBooleanColumn(String column) {
266: if (!hasColumn(column)) {
267: throw new IllegalArgumentException("No such column "
268: + column);
269: }
270:
271: String name = canonicalize(column);
272:
273: if (isColumnNull(name)) {
274: return false;
275: }
276:
277: Object value = data.get(name);
278:
279: // make sure that we tolerate integers or booleans
280: if (value == null) {
281: throw new IllegalArgumentException("Column " + column
282: + " not present");
283: }
284:
285: if ((value instanceof Boolean)) {
286: return ((Boolean) value).booleanValue();
287: } else if ((value instanceof Integer)) {
288: int i = ((Integer) value).intValue();
289:
290: if (i == 0) {
291: return false; // 0 is false
292: }
293:
294: return true; // nonzero is true
295: } else {
296: throw new IllegalArgumentException(
297: "Value is not a boolean or an integer");
298: }
299: }
300:
301: /**
302: * Return the date value of column.
303: *
304: * If the column's type is not a date, or the column does not exist, an
305: * IllegalArgumentException is thrown.
306: *
307: * @param column
308: * The column name (case-insensitive)
309: * @return - The date value of the column, or null if the column is an SQL
310: * null.
311: */
312: public java.util.Date getDateColumn(String column) {
313: if (!hasColumn(column)) {
314: throw new IllegalArgumentException("No such column "
315: + column);
316: }
317:
318: String name = canonicalize(column);
319:
320: if (isColumnNull(name)) {
321: return null;
322: }
323:
324: Object value = data.get(name);
325:
326: if (value == null) {
327: throw new IllegalArgumentException("Column " + column
328: + " not present");
329: }
330:
331: if (!(value instanceof java.util.Date)) {
332: throw new IllegalArgumentException("Value is not a Date");
333: }
334:
335: return (java.util.Date) value;
336: }
337:
338: /**
339: * Set column to an SQL NULL.
340: *
341: * If the column does not exist, an IllegalArgumentException is thrown.
342: *
343: * @param column
344: * The column name (case-insensitive)
345: */
346: public void setColumnNull(String column) {
347: if (!hasColumn(column)) {
348: throw new IllegalArgumentException("No such column "
349: + column);
350: }
351:
352: setColumnNullInternal(canonicalize(column));
353: }
354:
355: /**
356: * Set column to the boolean b.
357: *
358: * If the column does not exist, an IllegalArgumentException is thrown.
359: *
360: * @param column
361: * The column name (case-insensitive)
362: * @param b
363: * The boolean value
364: */
365: public void setColumn(String column, boolean b) {
366: if (!hasColumn(column)) {
367: throw new IllegalArgumentException("No such column "
368: + column);
369: }
370:
371: String canonName = canonicalize(column);
372: if ("oracle"
373: .equals(ConfigurationManager.getProperty("db.name"))) {
374: // if oracle, use 1 or 0 for true/false
375: Integer value = b ? new Integer(1) : new Integer(0);
376: if (!value.equals(data.get(canonName))) {
377: data.put(canonName, value);
378: changed.put(canonName, Boolean.TRUE);
379: }
380: } else {
381: // default to postgres true/false
382: Boolean value = b ? Boolean.TRUE : Boolean.FALSE;
383: if (!value.equals(data.get(canonName))) {
384: data.put(canonName, value);
385: changed.put(canonName, Boolean.TRUE);
386: }
387: }
388: }
389:
390: /**
391: * Set column to the String s. If s is null, the column is set to null.
392: *
393: * If the column does not exist, an IllegalArgumentException is thrown.
394: *
395: * @param column
396: * The column name (case-insensitive)
397: * @param s
398: * The String value
399: */
400: public void setColumn(String column, String s) {
401: if (!hasColumn(column)) {
402: throw new IllegalArgumentException("No such column "
403: + column);
404: }
405:
406: String canonName = canonicalize(column);
407: Object value = (s == null) ? NULL_OBJECT : s;
408: if (!value.equals(data.get(canonName))) {
409: data.put(canonName, value);
410: changed.put(canonName, Boolean.TRUE);
411: }
412: }
413:
414: /**
415: * Set column to the integer i.
416: *
417: * If the column does not exist, an IllegalArgumentException is thrown.
418: *
419: * @param column
420: * The column name (case-insensitive)
421: * @param i
422: * The integer value
423: */
424: public void setColumn(String column, int i) {
425: if (!hasColumn(column)) {
426: throw new IllegalArgumentException("No such column "
427: + column);
428: }
429:
430: String canonName = canonicalize(column);
431: Integer value = new Integer(i);
432: if (!value.equals(data.get(canonName))) {
433: data.put(canonName, value);
434: changed.put(canonName, Boolean.TRUE);
435: }
436: }
437:
438: /**
439: * Set column to the long l.
440: *
441: * If the column does not exist, an IllegalArgumentException is thrown.
442: *
443: * @param column
444: * The column name (case-insensitive)
445: * @param l
446: * The long value
447: */
448: public void setColumn(String column, long l) {
449: if (!hasColumn(column)) {
450: throw new IllegalArgumentException("No such column "
451: + column);
452: }
453:
454: String canonName = canonicalize(column);
455: Long value = new Long(l);
456: if (!value.equals(data.get(canonName))) {
457: data.put(canonName, value);
458: changed.put(canonName, Boolean.TRUE);
459: }
460: }
461:
462: /**
463: * Set column to the date d. If the date is null, the column is set to NULL
464: * as well.
465: *
466: * If the column does not exist, an IllegalArgumentException is thrown.
467: *
468: * @param column
469: * The column name (case-insensitive)
470: * @param d
471: * The date value
472: */
473: public void setColumn(String column, java.util.Date d) {
474: if (!hasColumn(column)) {
475: throw new IllegalArgumentException("No such column "
476: + column);
477: }
478:
479: String canonName = canonicalize(column);
480: Object value = (d == null) ? NULL_OBJECT : d;
481: if (!value.equals(data.get(canonName))) {
482: data.put(canonName, value);
483: changed.put(canonName, Boolean.TRUE);
484: }
485: }
486:
487: ////////////////////////////////////////
488: // Utility methods
489: ////////////////////////////////////////
490:
491: /**
492: * Return a String representation of this object.
493: *
494: * @return String representaton
495: */
496: public String toString() {
497: final String NEWLINE = System.getProperty("line.separator");
498: StringBuffer result;
499:
500: if (table == null) {
501: result = new StringBuffer("no_table");
502: } else {
503: result = new StringBuffer(table);
504: }
505:
506: result.append(NEWLINE);
507:
508: for (Iterator iterator = data.keySet().iterator(); iterator
509: .hasNext();) {
510: String column = (String) iterator.next();
511: result.append("\t").append(column).append(" = ").append(
512: isColumnNull(column) ? "NULL" : data.get(column))
513: .append(NEWLINE);
514: }
515:
516: return result.toString();
517: }
518:
519: /**
520: * Return a hash code for this object.
521: *
522: * @return int hash of object
523: */
524: public int hashCode() {
525: return toString().hashCode();
526: }
527:
528: /**
529: * Return true if this object equals obj, false otherwise.
530: *
531: * @param obj
532: * @return true if TableRow objects are equal
533: */
534: public boolean equals(Object obj) {
535: if (!(obj instanceof TableRow)) {
536: return false;
537: }
538:
539: return data.equals(((TableRow) obj).data);
540: }
541:
542: /**
543: * Return the canonical name for column.
544: *
545: * @param column
546: * The name of the column.
547: * @return The canonical name of the column.
548: */
549: static String canonicalize(String column) {
550: if ("oracle"
551: .equals(ConfigurationManager.getProperty("db.name"))) {
552: // oracle requires uppercase
553: return column.toUpperCase();
554: }
555:
556: // postgres default lowercase
557: return column.toLowerCase();
558: }
559:
560: /**
561: * Set columns to null.
562: *
563: * @param columns -
564: * A list of the columns to set to null. Each element of the list
565: * is a String.
566: */
567: private void nullColumns(List columns) {
568: for (Iterator iterator = columns.iterator(); iterator.hasNext();) {
569: setColumnNullInternal((String) iterator.next());
570: }
571: }
572:
573: private void resetChanged(List columns) {
574: for (Iterator iterator = columns.iterator(); iterator.hasNext();) {
575: changed.put(canonicalize((String) iterator.next()),
576: Boolean.FALSE);
577: }
578: }
579:
580: /**
581: * package private method to reset the flags of which columns have been updated
582: * This is used by the database manager after it has finished processing the contents
583: * of a resultset, so that it can update only columns that have been updated.
584: * Note that this method does not reset the values themselves, only the flags,
585: * and should not be considered safe to call from anywhere other than the DatabaseManager.
586: */
587: void resetChanged() {
588: for (Iterator iterator = changed.keySet().iterator(); iterator
589: .hasNext();) {
590: changed.put(canonicalize((String) iterator.next()),
591: Boolean.FALSE);
592: }
593: }
594:
595: /**
596: * Internal method to set column to null. The public method ensures that
597: * column actually exists.
598: *
599: * @param column
600: */
601: private void setColumnNullInternal(String column) {
602: String canonName = canonicalize(column);
603: if (data.get(canonName) != NULL_OBJECT) {
604: data.put(canonName, NULL_OBJECT);
605: changed.put(canonName, Boolean.TRUE);
606: }
607: }
608: }
|