1 package com.workingdogs.village;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayOutputStream;
23 import java.io.PrintWriter;
24
25 import java.math.BigDecimal;
26
27 import java.sql.Connection;
28 import java.sql.PreparedStatement;
29 import java.sql.ResultSet;
30 import java.sql.SQLException;
31
32 /***
33 * A Record represents a row in the database. It contains a collection of <a href="Value.html">Values</A> which are the individual
34 * contents of each column in the row.
35 *
36 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
37 * @version $Revision: 568 $
38 */
39 public class Record
40 {
41 /*** an array of Value objects, this is 1 based */
42 private Value [] values;
43
44 /*** a 1 To 1 relationship between Values and whether they are clean or not */
45 private boolean [] isClean;
46
47 /*** the parent DataSet for this Record */
48 private DataSet parentDataSet;
49
50 /*** number of columns in this Record */
51 private int numberOfColumns;
52
53 /*** this is the state of this record */
54 private int saveType = 0;
55
56 /*** a saved copy of the schema for this Record */
57 private Schema schema;
58
59 /***
60 * This isn't used and doesn't do anything.
61 */
62 public Record()
63 {
64
65 }
66
67 /***
68 * Creates a new Record and sets the parent dataset to the passed in value. This method also creates the Value objects which
69 * are associated with this Record.
70 *
71 * @param ds TODO: DOCUMENT ME!
72 *
73 * @throws DataSetException TODO: DOCUMENT ME!
74 * @throws SQLException TODO: DOCUMENT ME!
75 */
76 public Record(DataSet ds)
77 throws DataSetException, SQLException
78 {
79 setParentDataSet(ds);
80 initializeRecord();
81 createValues(dataset().resultSet());
82 }
83
84 /***
85 * This is a special case method for Record. This case is really only used when DataSet.addRecord() is called because we may
86 * not have an existing ResultSet so there will not be any values in the Value objects that are created. Passing null to
87 * createValues forces the Value object to be created, but no processing to be done within the Value object constructor.
88 *
89 * <P>
90 * This method is a package method only because it is really not useful outside of the package.
91 * </p>
92 *
93 * @param ds the dataset
94 * @param addRecord whether or not this method is being called from DataSet.addRecord()
95 *
96 * @throws DataSetException TODO: DOCUMENT ME!
97 * @throws SQLException TODO: DOCUMENT ME!
98 */
99 Record(DataSet ds, boolean addRecord)
100 throws DataSetException, SQLException
101 {
102 setParentDataSet(ds);
103 initializeRecord();
104 createValues(null);
105 }
106
107 /***
108 * Performs initialization for this Record.
109 *
110 * @throws DataSetException TODO: DOCUMENT ME!
111 */
112 private void initializeRecord()
113 throws DataSetException
114 {
115 this.schema = dataset().schema();
116 this.numberOfColumns = schema.numberOfColumns();
117 this.values = new Value[size() + 1];
118 this.isClean = new boolean[size() + 1];
119 setSaveType(Enums.UNKNOWN);
120
121 for (int i = 1; i <= size(); i++)
122 {
123 markValueClean(i);
124 this.values[i] = null;
125 }
126 }
127
128 /***
129 * Creates the value objects for this Record. It is 1 based
130 *
131 * @param rs TODO: DOCUMENT ME!
132 *
133 * @exception DataSetException
134 * @exception SQLException
135 */
136 private void createValues(ResultSet rs)
137 throws DataSetException, SQLException
138 {
139 for (int i = 1; i <= size(); i++)
140 {
141 Value val = new Value(rs, i, schema().column(i).typeEnum());
142 this.values[i] = val;
143 }
144 }
145
146 /***
147 * Saves the data in this Record to the database. Uses the parent dataset's connection.
148 *
149 * @return 1 if the save completed. 0 otherwise.
150 *
151 * @throws DataSetException TODO: DOCUMENT ME!
152 * @throws SQLException TODO: DOCUMENT ME!
153 */
154 public int save()
155 throws DataSetException, SQLException
156 {
157 return save(dataset().connection());
158 }
159
160 /***
161 * Saves the data in this Record to the database. Uses the connection passed into it.
162 *
163 * @param connection TODO: DOCUMENT ME!
164 *
165 * @return 1 if the save completed. 0 otherwise.
166 *
167 * @throws DataSetException TODO: DOCUMENT ME!
168 * @throws SQLException TODO: DOCUMENT ME!
169 */
170 public int save(Connection connection)
171 throws DataSetException, SQLException
172 {
173 int returnValue = 0;
174
175 if (dataset() instanceof QueryDataSet)
176 {
177 throw new DataSetException("You cannot save a QueryDataSet. Please use a TableDataSet instead.");
178 }
179
180 if (!needsToBeSaved())
181 {
182 return returnValue;
183 }
184
185 if (toBeSavedWithInsert())
186 {
187 returnValue = saveWithInsert(connection);
188 }
189 else if (toBeSavedWithUpdate())
190 {
191 returnValue = saveWithUpdate(connection);
192 }
193 else if (toBeSavedWithDelete())
194 {
195 returnValue = saveWithDelete(connection);
196 }
197
198 return returnValue;
199 }
200
201 /***
202 * Saves the data in this Record to the database with an DELETE statement
203 *
204 * @param connection TODO: DOCUMENT ME!
205 *
206 * @return SQL DELETE statement
207 *
208 * @throws DataSetException TODO: DOCUMENT ME!
209 * @throws SQLException TODO: DOCUMENT ME!
210 */
211 private int saveWithDelete(Connection connection)
212 throws DataSetException, SQLException
213 {
214 PreparedStatement stmt = null;
215
216 try
217 {
218 stmt = connection.prepareStatement(getSaveString());
219
220 int ps = 1;
221
222 for (int i = 1; i <= dataset().keydef().size(); i++)
223 {
224 Value val = getValue(dataset().keydef().getAttrib(i));
225
226 val.setPreparedStatementValue(stmt, ps++);
227 }
228
229 int ret = stmt.executeUpdate();
230
231
232
233
234
235
236
237
238 setSaveType(Enums.ZOMBIE);
239
240 if (ret > 1)
241 {
242 throw new SQLException("There were " + ret + " rows deleted with this records key value.");
243 }
244
245 return ret;
246 }
247 catch (SQLException e1)
248 {
249 throw e1;
250 }
251 finally
252 {
253 try
254 {
255 if (stmt != null)
256 {
257 stmt.close();
258 }
259 }
260 catch (SQLException e2)
261 {
262 throw e2;
263 }
264 }
265 }
266
267 /***
268 * Saves the data in this Record to the database with an UPDATE statement
269 *
270 * @param connection TODO: DOCUMENT ME!
271 *
272 * @return SQL UPDATE statement
273 *
274 * @throws DataSetException TODO: DOCUMENT ME!
275 * @throws SQLException TODO: DOCUMENT ME!
276 */
277 private int saveWithUpdate(Connection connection)
278 throws DataSetException, SQLException
279 {
280 PreparedStatement stmt = null;
281
282 try
283 {
284 stmt = connection.prepareStatement(getSaveString());
285
286 int ps = 1;
287
288 for (int i = 1; i <= size(); i++)
289 {
290 Value val = getValue(i);
291
292 if (!valueIsClean(i) && !schema().column(i).readOnly())
293 {
294 val.setPreparedStatementValue(stmt, ps++);
295 }
296 }
297
298 for (int i = 1; i <= dataset().keydef().size(); i++)
299 {
300 Value val = getValue(dataset().keydef().getAttrib(i));
301
302 val.setPreparedStatementValue(stmt, ps++);
303 }
304
305 int ret = stmt.executeUpdate();
306
307 if (((TableDataSet) dataset()).refreshOnSave())
308 {
309 refresh(dataset().connection());
310 }
311 else
312 {
313
314 markRecordClean();
315 }
316
317 setSaveType(Enums.AFTERUPDATE);
318
319 if (ret > 1)
320 {
321 throw new SQLException("There were " + ret + " rows updated with this records key value.");
322 }
323
324 return ret;
325 }
326 catch (SQLException e1)
327 {
328 throw e1;
329 }
330 finally
331 {
332 try
333 {
334 if (stmt != null)
335 {
336 stmt.close();
337 }
338 }
339 catch (SQLException e2)
340 {
341 throw e2;
342 }
343 }
344 }
345
346 /***
347 * Saves the data in this Record to the database with an INSERT statement
348 *
349 * @param connection TODO: DOCUMENT ME!
350 *
351 * @return SQL INSERT statement
352 *
353 * @throws DataSetException TODO: DOCUMENT ME!
354 * @throws SQLException TODO: DOCUMENT ME!
355 */
356 private int saveWithInsert(Connection connection)
357 throws DataSetException, SQLException
358 {
359 PreparedStatement stmt = null;
360
361 try
362 {
363 stmt = connection.prepareStatement(getSaveString());
364
365 int ps = 1;
366
367 for (int i = 1; i <= size(); i++)
368 {
369 Value val = getValue(i);
370
371 if (!valueIsClean(i) && !schema().column(i).readOnly())
372 {
373 val.setPreparedStatementValue(stmt, ps++);
374 }
375 }
376
377 int ret = stmt.executeUpdate();
378
379 if (((TableDataSet) dataset()).refreshOnSave())
380 {
381 refresh(dataset().connection());
382 }
383 else
384 {
385
386 markRecordClean();
387 }
388
389 setSaveType(Enums.AFTERINSERT);
390
391 if (ret > 1)
392 {
393 throw new SQLException("There were " + ret + " rows inserted with this records key value.");
394 }
395
396 return ret;
397 }
398 catch (SQLException e1)
399 {
400 throw e1;
401 }
402 finally
403 {
404 try
405 {
406 if (stmt != null)
407 {
408 stmt.close();
409 }
410 }
411 catch (SQLException e2)
412 {
413 throw e2;
414 }
415 }
416 }
417
418 /***
419 * Builds the SQL UPDATE statement for this Record
420 *
421 * @return SQL UPDATE statement
422 *
423 * @throws DataSetException TODO: DOCUMENT ME!
424 */
425 private String getUpdateSaveString()
426 throws DataSetException
427 {
428 KeyDef kd = dataset().keydef();
429
430 if ((kd == null) || (kd.size() == 0))
431 {
432 throw new DataSetException(
433 "You must specify KeyDef attributes for this TableDataSet in order to create a Record for update.");
434 }
435 else if (recordIsClean())
436 {
437 throw new DataSetException("You must Record.setValue() on a column before doing an update.");
438 }
439
440 StringBuffer iss1 = new StringBuffer(256);
441 StringBuffer iss2 = new StringBuffer(256);
442 boolean comma = false;
443
444 for (int i = 1; i <= size(); i++)
445 {
446 if (!valueIsClean(i) && !schema().column(i).readOnly())
447 {
448 if (!comma)
449 {
450 iss1.append(schema().column(i).name());
451 iss1.append(" = ?");
452 comma = true;
453 }
454 else
455 {
456 iss1.append(", ");
457 iss1.append(schema().column(i).name());
458 iss1.append(" = ?");
459 }
460 }
461 }
462
463 comma = false;
464
465 for (int i = 1; i <= kd.size(); i++)
466 {
467 String attrib = kd.getAttrib(i);
468
469 if (!valueIsClean(schema().index(attrib)))
470 {
471 throw new DataSetException("The value for column '" + attrib + "' is a key value and cannot be updated.");
472 }
473
474 if (!comma)
475 {
476 iss2.append(attrib);
477 iss2.append(" = ?");
478 comma = true;
479 }
480 else
481 {
482 iss2.append(" AND ");
483 iss2.append(attrib);
484 iss2.append(" = ?");
485 }
486 }
487
488 return "UPDATE " + schema().tableName() + " SET " + iss1.toString() + " WHERE " + iss2.toString();
489 }
490
491 /***
492 * Builds the SQL DELETE statement for this Record
493 *
494 * @return SQL DELETE statement
495 *
496 * @throws DataSetException TODO: DOCUMENT ME!
497 */
498 private String getDeleteSaveString()
499 throws DataSetException
500 {
501 KeyDef kd = dataset().keydef();
502
503 if ((kd == null) || (kd.size() == 0))
504 {
505 throw new DataSetException("You must specify KeyDef attributes for this TableDataSet in order to delete a Record.");
506 }
507
508 StringBuffer iss1 = new StringBuffer(256);
509
510 boolean comma = false;
511
512 for (int i = 1; i <= kd.size(); i++)
513 {
514 if (!comma)
515 {
516 iss1.append(kd.getAttrib(i));
517 iss1.append(" = ?");
518 comma = true;
519 }
520 else
521 {
522 iss1.append(" AND ");
523 iss1.append(kd.getAttrib(i));
524 iss1.append(" = ? ");
525 }
526 }
527
528 return "DELETE FROM " + schema().tableName() + " WHERE " + iss1.toString();
529 }
530
531 /***
532 * Builds the SQL INSERT statement for this Record
533 *
534 * @return SQL INSERT statement
535 *
536 * @throws DataSetException TODO: DOCUMENT ME!
537 */
538 private String getInsertSaveString()
539 throws DataSetException
540 {
541 StringBuffer iss1 = new StringBuffer(256);
542 StringBuffer iss2 = new StringBuffer(256);
543
544 boolean comma = false;
545
546 for (int i = 1; i <= size(); i++)
547 {
548 if (!valueIsClean(i) && !schema().column(i).readOnly())
549 {
550 if (!comma)
551 {
552 iss1.append(schema().column(i).name());
553 iss2.append("?");
554 comma = true;
555 }
556 else
557 {
558 iss1.append(", " + schema().column(i).name());
559 iss2.append(", ?");
560 }
561 }
562 }
563
564 return "INSERT INTO " + schema().tableName() + " ( " + iss1.toString() + " ) VALUES ( " + iss2.toString() + " )";
565 }
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581 /***
582 * Gets the appropriate SQL string for this record.
583 *
584 * @return SQL string
585 *
586 * @throws DataSetException TODO: DOCUMENT ME!
587 */
588 public String getSaveString()
589 throws DataSetException
590 {
591 if (toBeSavedWithInsert())
592 {
593 return getInsertSaveString();
594 }
595 else if (toBeSavedWithUpdate())
596 {
597 return getUpdateSaveString();
598 }
599 else if (toBeSavedWithDelete())
600 {
601 return getDeleteSaveString();
602 }
603 else
604 {
605 throw new DataSetException("Not able to return save string: " + this.saveType);
606 }
607 }
608
609 /***
610 * gets the value at index i
611 *
612 * @param i TODO: DOCUMENT ME!
613 *
614 * @return the Value object at index i
615 *
616 * @throws DataSetException TODO: DOCUMENT ME!
617 */
618 public Value getValue(int i)
619 throws DataSetException
620 {
621 if (i == 0)
622 {
623 throw new DataSetException("Values are 1 based!");
624 }
625 else if (i > size())
626 {
627 throw new DataSetException("Only " + size() + " columns exist!");
628 }
629 else if (values[i] == null)
630 {
631 throw new DataSetException("No values for the requested column!");
632 }
633
634 return values[i];
635 }
636
637 /***
638 * TODO: DOCUMENT ME!
639 *
640 * @param columnName TODO: DOCUMENT ME!
641 *
642 * @return TODO: DOCUMENT ME!
643 *
644 * @throws DataSetException TODO: DOCUMENT ME!
645 */
646 public Value getValue(String columnName)
647 throws DataSetException
648 {
649 return getValue(schema().index(columnName));
650 }
651
652 /***
653 * the number of columns in this object
654 *
655 * @return the number of columns in this object
656 */
657 public int size()
658 {
659 return numberOfColumns;
660 }
661
662 /***
663 * whether or not this Record is to be saved with an SQL insert statement
664 *
665 * @return true if saved with insert
666 */
667 public boolean toBeSavedWithInsert()
668 {
669 return (this.saveType == Enums.INSERT) ? true : false;
670 }
671
672 /***
673 * whether or not this Record is to be saved with an SQL update statement
674 *
675 * @return true if saved with update
676 */
677 public boolean toBeSavedWithUpdate()
678 {
679 return (this.saveType == Enums.UPDATE) ? true : false;
680 }
681
682 /***
683 * whether or not this Record is to be saved with an SQL delete statement
684 *
685 * @return true if saved with delete
686 */
687 public boolean toBeSavedWithDelete()
688 {
689 return (this.saveType == Enums.DELETE) ? true : false;
690 }
691
692 /***
693 * Marks all the values in this record as clean.
694 *
695 * @throws DataSetException TODO: DOCUMENT ME!
696 */
697 public void markRecordClean()
698 throws DataSetException
699 {
700 for (int i = 1; i <= size(); i++)
701 {
702 markValueClean(i);
703 }
704 }
705
706 /***
707 * Marks this record to be inserted when a save is executed.
708 *
709 * @throws DataSetException TODO: DOCUMENT ME!
710 */
711 public void markForInsert()
712 throws DataSetException
713 {
714 if (dataset() instanceof QueryDataSet)
715 {
716 throw new DataSetException("You cannot mark a record in a QueryDataSet for insert");
717 }
718
719 setSaveType(Enums.INSERT);
720 }
721
722 /***
723 * Marks this record to be updated when a save is executed.
724 *
725 * @throws DataSetException TODO: DOCUMENT ME!
726 */
727 public void markForUpdate()
728 throws DataSetException
729 {
730 if (dataset() instanceof QueryDataSet)
731 {
732 throw new DataSetException("You cannot mark a record in a QueryDataSet for update");
733 }
734
735 setSaveType(Enums.UPDATE);
736 }
737
738 /***
739 * Marks this record to be deleted when a save is executed.
740 *
741 * @return TODO: DOCUMENT ME!
742 *
743 * @throws DataSetException TODO: DOCUMENT ME!
744 */
745 public Record markToBeDeleted()
746 throws DataSetException
747 {
748 if (dataset() instanceof QueryDataSet)
749 {
750 throw new DataSetException("You cannot mark a record in a QueryDataSet for deletion");
751 }
752
753 setSaveType(Enums.DELETE);
754
755 return this;
756 }
757
758 /***
759 * Unmarks a record that has been marked for deletion.
760 *
761 * <P>
762 * WARNING: You must reset the save type before trying to save this record again.
763 * </p>
764 *
765 * @return TODO: DOCUMENT ME!
766 *
767 * @throws DataSetException TODO: DOCUMENT ME!
768 *
769 * @see #markForUpdate()
770 * @see #markForInsert()
771 * @see #markToBeDeleted()
772 */
773 public Record unmarkToBeDeleted()
774 throws DataSetException
775 {
776 if (this.saveType == Enums.ZOMBIE)
777 {
778 throw new DataSetException("This record has already been deleted!");
779 }
780
781 setSaveType(Enums.UNKNOWN);
782
783 return this;
784 }
785
786 /***
787 * marks a value at a given position as clean.
788 *
789 * @param pos TODO: DOCUMENT ME!
790 *
791 * @throws DataSetException TODO: DOCUMENT ME!
792 */
793 public void markValueClean(int pos)
794 throws DataSetException
795 {
796 if (pos == 0)
797 {
798 throw new DataSetException("Value position must be greater than 0.");
799 }
800 else if (pos > size())
801 {
802 throw new DataSetException("Value position is greater than number of values.");
803 }
804
805 this.isClean[pos] = true;
806 }
807
808 /***
809 * marks a value with a given column name as clean.
810 *
811 * @param columnName TODO: DOCUMENT ME!
812 *
813 * @throws DataSetException TODO: DOCUMENT ME!
814 */
815 public void markValueClean(String columnName)
816 throws DataSetException
817 {
818 markValueClean(schema().index(columnName));
819 }
820
821 /***
822 * marks a value at a given position as dirty.
823 *
824 * @param pos TODO: DOCUMENT ME!
825 *
826 * @throws DataSetException TODO: DOCUMENT ME!
827 */
828 public void markValueDirty(int pos)
829 throws DataSetException
830 {
831 if (pos == 0)
832 {
833 throw new DataSetException("Value position must be greater than 0.");
834 }
835 else if (pos > size())
836 {
837 throw new DataSetException("Value position is greater than number of values.");
838 }
839
840 this.isClean[pos] = false;
841 }
842
843 /***
844 * marks a value with a given column name as dirty.
845 *
846 * @param columnName TODO: DOCUMENT ME!
847 *
848 * @throws DataSetException TODO: DOCUMENT ME!
849 */
850 public void markValueDirty(String columnName)
851 throws DataSetException
852 {
853 markValueDirty(schema().index(columnName));
854 }
855
856 /***
857 * sets the internal save type as one of the defined privates (ie: ZOMBIE)
858 *
859 * @param type TODO: DOCUMENT ME!
860 */
861 void setSaveType(int type)
862 {
863 this.saveType = type;
864 }
865
866 /***
867 * gets the internal save type as one of the defined privates (ie: ZOMBIE)
868 *
869 * @return TODO: DOCUMENT ME!
870 */
871 int getSaveType()
872 {
873 return this.saveType;
874 }
875
876 /***
877 * sets the value at pos with a BigDecimal
878 *
879 * @param pos TODO: DOCUMENT ME!
880 * @param value TODO: DOCUMENT ME!
881 *
882 * @return TODO: DOCUMENT ME!
883 *
884 * @throws DataSetException TODO: DOCUMENT ME!
885 */
886 public Record setValue(int pos, BigDecimal value)
887 throws DataSetException
888 {
889 this.values[pos].setValue(value);
890 markValueDirty(pos);
891
892 return this;
893 }
894
895 /***
896 * sets the value at pos with a boolean
897 *
898 * @param pos TODO: DOCUMENT ME!
899 * @param value TODO: DOCUMENT ME!
900 *
901 * @return TODO: DOCUMENT ME!
902 *
903 * @throws DataSetException TODO: DOCUMENT ME!
904 */
905 public Record setValue(int pos, boolean value)
906 throws DataSetException
907 {
908 this.values[pos].setValue(new Boolean(value));
909 markValueDirty(pos);
910
911 return this;
912 }
913
914 /***
915 * sets the value at pos with a byte[]
916 *
917 * @param pos TODO: DOCUMENT ME!
918 * @param value TODO: DOCUMENT ME!
919 *
920 * @return TODO: DOCUMENT ME!
921 *
922 * @throws DataSetException TODO: DOCUMENT ME!
923 */
924 public Record setValue(int pos, byte [] value)
925 throws DataSetException
926 {
927 this.values[pos].setValue(value);
928 markValueDirty(pos);
929
930 return this;
931 }
932
933 /***
934 * sets the value at pos with a java.util.Date
935 *
936 * @param pos TODO: DOCUMENT ME!
937 * @param value TODO: DOCUMENT ME!
938 *
939 * @return TODO: DOCUMENT ME!
940 *
941 * @throws DataSetException TODO: DOCUMENT ME!
942 */
943 public Record setValue(int pos, java.util.Date value)
944 throws DataSetException
945 {
946 this.values[pos].setValue(value);
947 markValueDirty(pos);
948
949 return this;
950 }
951
952 /***
953 * sets the value at pos with a java.sql.Date
954 *
955 * @param pos TODO: DOCUMENT ME!
956 * @param value TODO: DOCUMENT ME!
957 *
958 * @return TODO: DOCUMENT ME!
959 *
960 * @throws DataSetException TODO: DOCUMENT ME!
961 */
962 public Record setValue(int pos, java.sql.Date value)
963 throws DataSetException
964 {
965 this.values[pos].setValue(value);
966 markValueDirty(pos);
967
968 return this;
969 }
970
971 /***
972 * sets the value at pos with a double
973 *
974 * @param pos TODO: DOCUMENT ME!
975 * @param value TODO: DOCUMENT ME!
976 *
977 * @return TODO: DOCUMENT ME!
978 *
979 * @throws DataSetException TODO: DOCUMENT ME!
980 */
981 public Record setValue(int pos, double value)
982 throws DataSetException
983 {
984 this.values[pos].setValue(new Double(value));
985 markValueDirty(pos);
986
987 return this;
988 }
989
990 /***
991 * sets the value at pos with a float
992 *
993 * @param pos TODO: DOCUMENT ME!
994 * @param value TODO: DOCUMENT ME!
995 *
996 * @return TODO: DOCUMENT ME!
997 *
998 * @throws DataSetException TODO: DOCUMENT ME!
999 */
1000 public Record setValue(int pos, float value)
1001 throws DataSetException
1002 {
1003 this.values[pos].setValue(new Float(value));
1004 markValueDirty(pos);
1005
1006 return this;
1007 }
1008
1009 /***
1010 * sets the value at pos with a int
1011 *
1012 * @param pos TODO: DOCUMENT ME!
1013 * @param value TODO: DOCUMENT ME!
1014 *
1015 * @return TODO: DOCUMENT ME!
1016 *
1017 * @throws DataSetException TODO: DOCUMENT ME!
1018 */
1019 public Record setValue(int pos, int value)
1020 throws DataSetException
1021 {
1022 this.values[pos].setValue(new Integer(value));
1023 markValueDirty(pos);
1024
1025 return this;
1026 }
1027
1028 /***
1029 * sets the value at pos with a long
1030 *
1031 * @param pos TODO: DOCUMENT ME!
1032 * @param value TODO: DOCUMENT ME!
1033 *
1034 * @return TODO: DOCUMENT ME!
1035 *
1036 * @throws DataSetException TODO: DOCUMENT ME!
1037 */
1038 public Record setValue(int pos, long value)
1039 throws DataSetException
1040 {
1041 this.values[pos].setValue(new Long(value));
1042 markValueDirty(pos);
1043
1044 return this;
1045 }
1046
1047 /***
1048 * sets the value at pos with a String
1049 *
1050 * @param pos TODO: DOCUMENT ME!
1051 * @param value TODO: DOCUMENT ME!
1052 *
1053 * @return TODO: DOCUMENT ME!
1054 *
1055 * @throws DataSetException TODO: DOCUMENT ME!
1056 */
1057 public Record setValue(int pos, String value)
1058 throws DataSetException
1059 {
1060 this.values[pos].setValue(value);
1061 markValueDirty(pos);
1062
1063 return this;
1064 }
1065
1066 /***
1067 * sets the value at pos with a java.sql.Time
1068 *
1069 * @param pos TODO: DOCUMENT ME!
1070 * @param value TODO: DOCUMENT ME!
1071 *
1072 * @return TODO: DOCUMENT ME!
1073 *
1074 * @throws DataSetException TODO: DOCUMENT ME!
1075 */
1076 public Record setValue(int pos, java.sql.Time value)
1077 throws DataSetException
1078 {
1079 this.values[pos].setValue(value);
1080 markValueDirty(pos);
1081
1082 return this;
1083 }
1084
1085 /***
1086 * sets the value at pos with a java.sql.Timestamp
1087 *
1088 * @param pos TODO: DOCUMENT ME!
1089 * @param value TODO: DOCUMENT ME!
1090 *
1091 * @return TODO: DOCUMENT ME!
1092 *
1093 * @throws DataSetException TODO: DOCUMENT ME!
1094 */
1095 public Record setValue(int pos, java.sql.Timestamp value)
1096 throws DataSetException
1097 {
1098 this.values[pos].setValue(value);
1099 markValueDirty(pos);
1100
1101 return this;
1102 }
1103
1104 /***
1105 * sets the value at pos with a Value
1106 *
1107 * @param pos TODO: DOCUMENT ME!
1108 * @param value TODO: DOCUMENT ME!
1109 *
1110 * @return TODO: DOCUMENT ME!
1111 *
1112 * @throws DataSetException TODO: DOCUMENT ME!
1113 */
1114 public Record setValue(int pos, Value value)
1115 throws DataSetException
1116 {
1117 this.values[pos].setValue(value.getValue());
1118 markValueDirty(pos);
1119
1120 return this;
1121 }
1122
1123 /***
1124 * sets the value at column name with a BigDecimal
1125 *
1126 * @param columnName TODO: DOCUMENT ME!
1127 * @param value TODO: DOCUMENT ME!
1128 *
1129 * @return TODO: DOCUMENT ME!
1130 *
1131 * @throws DataSetException TODO: DOCUMENT ME!
1132 */
1133 public Record setValue(String columnName, BigDecimal value)
1134 throws DataSetException
1135 {
1136 setValue(schema().index(columnName), value);
1137
1138 return this;
1139 }
1140
1141 /***
1142 * sets the value at column name with a boolean
1143 *
1144 * @param columnName TODO: DOCUMENT ME!
1145 * @param value TODO: DOCUMENT ME!
1146 *
1147 * @return TODO: DOCUMENT ME!
1148 *
1149 * @throws DataSetException TODO: DOCUMENT ME!
1150 */
1151 public Record setValue(String columnName, boolean value)
1152 throws DataSetException
1153 {
1154 setValue(schema().index(columnName), value);
1155
1156 return this;
1157 }
1158
1159 /***
1160 * sets the value at column name with a byte[]
1161 *
1162 * @param columnName TODO: DOCUMENT ME!
1163 * @param value TODO: DOCUMENT ME!
1164 *
1165 * @return TODO: DOCUMENT ME!
1166 *
1167 * @throws DataSetException TODO: DOCUMENT ME!
1168 */
1169 public Record setValue(String columnName, byte [] value)
1170 throws DataSetException
1171 {
1172 setValue(schema().index(columnName), value);
1173
1174 return this;
1175 }
1176
1177 /***
1178 * sets the value at column name with a java.util.Date
1179 *
1180 * @param columnName TODO: DOCUMENT ME!
1181 * @param value TODO: DOCUMENT ME!
1182 *
1183 * @return TODO: DOCUMENT ME!
1184 *
1185 * @throws DataSetException TODO: DOCUMENT ME!
1186 */
1187 public Record setValue(String columnName, java.util.Date value)
1188 throws DataSetException
1189 {
1190 setValue(schema().index(columnName), value);
1191
1192 return this;
1193 }
1194
1195 /***
1196 * sets the value at column name with a java.sql.Date
1197 *
1198 * @param columnName TODO: DOCUMENT ME!
1199 * @param value TODO: DOCUMENT ME!
1200 *
1201 * @return TODO: DOCUMENT ME!
1202 *
1203 * @throws DataSetException TODO: DOCUMENT ME!
1204 */
1205 public Record setValue(String columnName, java.sql.Date value)
1206 throws DataSetException
1207 {
1208 setValue(schema().index(columnName), value);
1209
1210 return this;
1211 }
1212
1213 /***
1214 * sets the value at column name with a double
1215 *
1216 * @param columnName TODO: DOCUMENT ME!
1217 * @param value TODO: DOCUMENT ME!
1218 *
1219 * @return TODO: DOCUMENT ME!
1220 *
1221 * @throws DataSetException TODO: DOCUMENT ME!
1222 */
1223 public Record setValue(String columnName, double value)
1224 throws DataSetException
1225 {
1226 setValue(schema().index(columnName), value);
1227
1228 return this;
1229 }
1230
1231 /***
1232 * sets the value at column name with a float
1233 *
1234 * @param columnName TODO: DOCUMENT ME!
1235 * @param value TODO: DOCUMENT ME!
1236 *
1237 * @return TODO: DOCUMENT ME!
1238 *
1239 * @throws DataSetException TODO: DOCUMENT ME!
1240 */
1241 public Record setValue(String columnName, float value)
1242 throws DataSetException
1243 {
1244 setValue(schema().index(columnName), value);
1245
1246 return this;
1247 }
1248
1249 /***
1250 * sets the value at column name with a int
1251 *
1252 * @param columnName TODO: DOCUMENT ME!
1253 * @param value TODO: DOCUMENT ME!
1254 *
1255 * @return TODO: DOCUMENT ME!
1256 *
1257 * @throws DataSetException TODO: DOCUMENT ME!
1258 */
1259 public Record setValue(String columnName, int value)
1260 throws DataSetException
1261 {
1262 setValue(schema().index(columnName), value);
1263
1264 return this;
1265 }
1266
1267 /***
1268 * sets the value at column name with a long
1269 *
1270 * @param columnName TODO: DOCUMENT ME!
1271 * @param value TODO: DOCUMENT ME!
1272 *
1273 * @return TODO: DOCUMENT ME!
1274 *
1275 * @throws DataSetException TODO: DOCUMENT ME!
1276 */
1277 public Record setValue(String columnName, long value)
1278 throws DataSetException
1279 {
1280 setValue(schema().index(columnName), value);
1281
1282 return this;
1283 }
1284
1285 /***
1286 * sets the value at column name with a String
1287 *
1288 * @param columnName TODO: DOCUMENT ME!
1289 * @param value TODO: DOCUMENT ME!
1290 *
1291 * @return TODO: DOCUMENT ME!
1292 *
1293 * @throws DataSetException TODO: DOCUMENT ME!
1294 */
1295 public Record setValue(String columnName, String value)
1296 throws DataSetException
1297 {
1298 setValue(schema().index(columnName), value);
1299
1300 return this;
1301 }
1302
1303 /***
1304 * sets the value at column name with a java.sql.Time
1305 *
1306 * @param columnName TODO: DOCUMENT ME!
1307 * @param value TODO: DOCUMENT ME!
1308 *
1309 * @return TODO: DOCUMENT ME!
1310 *
1311 * @throws DataSetException TODO: DOCUMENT ME!
1312 */
1313 public Record setValue(String columnName, java.sql.Time value)
1314 throws DataSetException
1315 {
1316 setValue(schema().index(columnName), value);
1317
1318 return this;
1319 }
1320
1321 /***
1322 * sets the value at column name with a java.sql.Timestamp
1323 *
1324 * @param columnName TODO: DOCUMENT ME!
1325 * @param value TODO: DOCUMENT ME!
1326 *
1327 * @return TODO: DOCUMENT ME!
1328 *
1329 * @throws DataSetException TODO: DOCUMENT ME!
1330 */
1331 public Record setValue(String columnName, java.sql.Timestamp value)
1332 throws DataSetException
1333 {
1334 setValue(schema().index(columnName), value);
1335
1336 return this;
1337 }
1338
1339 /***
1340 * sets the value at column name with a Value
1341 *
1342 * @param columnName TODO: DOCUMENT ME!
1343 * @param value TODO: DOCUMENT ME!
1344 *
1345 * @return TODO: DOCUMENT ME!
1346 *
1347 * @throws DataSetException TODO: DOCUMENT ME!
1348 */
1349 public Record setValue(String columnName, Value value)
1350 throws DataSetException
1351 {
1352 setValue(schema().index(columnName), value);
1353
1354 return this;
1355 }
1356
1357 /***
1358 * sets the value at pos with a NULL
1359 *
1360 * @param pos TODO: DOCUMENT ME!
1361 *
1362 * @return TODO: DOCUMENT ME!
1363 *
1364 * @throws DataSetException TODO: DOCUMENT ME!
1365 */
1366 public Record setValueNull(int pos)
1367 throws DataSetException
1368 {
1369 if (pos == 0)
1370 {
1371 throw new DataSetException("Value position must be greater than 0.");
1372 }
1373 else if (pos > size())
1374 {
1375 throw new DataSetException("Value position is greater than number of values.");
1376 }
1377
1378 this.values[pos].setValue(null);
1379 markValueDirty(pos);
1380
1381 return this;
1382 }
1383
1384 /***
1385 * sets the value at column name with a NULL
1386 *
1387 * @param columnName TODO: DOCUMENT ME!
1388 *
1389 * @return TODO: DOCUMENT ME!
1390 *
1391 * @throws DataSetException TODO: DOCUMENT ME!
1392 */
1393 public Record setValueNull(String columnName)
1394 throws DataSetException
1395 {
1396 if ((columnName == null) || (columnName.length() == 0))
1397 {
1398 throw new DataSetException("You must specify a column name!");
1399 }
1400
1401 setValueNull(schema().index(columnName));
1402
1403 return this;
1404 }
1405
1406 /***
1407 * Determines if this record is a Zombie. A Zombie is a record that has been deleted from the database, but not yet removed
1408 * from the DataSet.
1409 *
1410 * @return a boolean
1411 */
1412 public boolean isAZombie()
1413 {
1414 return (this.saveType == Enums.ZOMBIE) ? true : false;
1415 }
1416
1417 /***
1418 * If the record is not clean, needs to be saved with an Update, Delete or Insert, it returns true.
1419 *
1420 * @return boolean
1421 */
1422 public boolean needsToBeSaved()
1423 {
1424 return !isAZombie() || !recordIsClean() || toBeSavedWithUpdate() || toBeSavedWithDelete() || toBeSavedWithInsert();
1425 }
1426
1427 /***
1428 * Determines whether or not a value stored in the record is clean.
1429 *
1430 * @param i TODO: DOCUMENT ME!
1431 *
1432 * @return true if clean
1433 */
1434 public boolean valueIsClean(int i)
1435 {
1436 return isClean[i];
1437 }
1438
1439 /***
1440 * Determines whether or not a value stored in the record is clean.
1441 *
1442 * @param column TODO: DOCUMENT ME!
1443 *
1444 * @return true if clean
1445 *
1446 * @throws DataSetException TODO: DOCUMENT ME!
1447 */
1448 boolean valueIsClean(String column)
1449 throws DataSetException
1450 {
1451 return isClean[getValue(column).columnNumber()];
1452 }
1453
1454 /***
1455 * Goes through all the values in the record to determine if it is clean or not.
1456 *
1457 * @return true if clean
1458 */
1459 public boolean recordIsClean()
1460 {
1461 for (int i = 1; i <= size(); i++)
1462 {
1463 if (!valueIsClean(i))
1464 {
1465 return false;
1466 }
1467 }
1468
1469 return true;
1470 }
1471
1472 /***
1473 * This method refreshes this Record's Value's. It can only be performed on a Record that has not been modified and has been
1474 * created with a TableDataSet and corresponding KeyDef.
1475 *
1476 * @param connection
1477 *
1478 * @exception DataSetException
1479 * @exception SQLException
1480 */
1481 public void refresh(Connection connection)
1482 throws DataSetException, SQLException
1483 {
1484 if (toBeSavedWithDelete())
1485 {
1486 return;
1487 }
1488 else if (toBeSavedWithInsert())
1489 {
1490 throw new DataSetException("There is no way to refresh a record which has been created with addRecord().");
1491 }
1492 else if (dataset() instanceof QueryDataSet)
1493 {
1494 throw new DataSetException("You can only perform a refresh on Records created with a TableDataSet.");
1495 }
1496
1497 PreparedStatement stmt = null;
1498
1499 try
1500 {
1501 stmt = connection.prepareStatement(getRefreshQueryString());
1502
1503 int ps = 1;
1504
1505 for (int i = 1; i <= dataset().keydef().size(); i++)
1506 {
1507 Value val = getValue(dataset().keydef().getAttrib(i));
1508
1509 if (val.isNull())
1510 {
1511 throw new DataSetException("You cannot execute an update with a null value for a KeyDef.");
1512 }
1513
1514 val.setPreparedStatementValue(stmt, ps++);
1515 }
1516
1517 ResultSet rs = stmt.executeQuery();
1518 rs.next();
1519
1520 initializeRecord();
1521
1522 createValues(rs);
1523 }
1524 catch (SQLException e1)
1525 {
1526 throw e1;
1527 }
1528 finally
1529 {
1530 try
1531 {
1532 if (stmt != null)
1533 {
1534 stmt.close();
1535 }
1536 }
1537 catch (SQLException e2)
1538 {
1539 throw e2;
1540 }
1541 }
1542 }
1543
1544 /***
1545 * This builds the SELECT statement in order to refresh the contents of this Record. It depends on a valid KeyDef to exist and
1546 * it must have been created with a TableDataSet.
1547 *
1548 * @return the SELECT string
1549 *
1550 * @exception DataSetException
1551 */
1552 public String getRefreshQueryString()
1553 throws DataSetException
1554 {
1555 if ((dataset().keydef() == null) || (dataset().keydef().size() == 0))
1556 {
1557 throw new DataSetException(
1558 "You can only perform a getRefreshQueryString on a TableDataSet that was created with a KeyDef.");
1559 }
1560 else if (dataset() instanceof QueryDataSet)
1561 {
1562 throw new DataSetException("You can only perform a getRefreshQueryString on Records created with a TableDataSet.");
1563 }
1564
1565 StringBuffer iss1 = new StringBuffer(256);
1566 StringBuffer iss2 = new StringBuffer(256);
1567 boolean comma = false;
1568
1569 for (int i = 1; i <= size(); i++)
1570 {
1571 if (!comma)
1572 {
1573 iss1.append(schema().column(i).name());
1574 comma = true;
1575 }
1576 else
1577 {
1578 iss1.append(", ");
1579 iss1.append(schema().column(i).name());
1580 }
1581 }
1582
1583 comma = false;
1584
1585 for (int i = 1; i <= dataset().keydef().size(); i++)
1586 {
1587 String attrib = dataset().keydef().getAttrib(i);
1588
1589 if (!valueIsClean(attrib))
1590 {
1591 throw new DataSetException("You cannot do a refresh from the database if the value "
1592 + "for a KeyDef column has been changed with a Record.setValue().");
1593 }
1594
1595 if (!comma)
1596 {
1597 iss2.append(attrib);
1598 iss2.append(" = ?");
1599 comma = true;
1600 }
1601 else
1602 {
1603 iss2.append(" AND ");
1604 iss2.append(attrib);
1605 iss2.append(" = ?");
1606 }
1607 }
1608
1609 return "SELECT " + iss1.toString() + " FROM " + schema().tableName() + " WHERE " + iss2.toString();
1610 }
1611
1612 /***
1613 * TODO: DOCUMENT ME!
1614 *
1615 * @throws DataSetException TODO: DOCUMENT ME!
1616 */
1617 public void saveWithoutStatusUpdate()
1618 throws DataSetException
1619 {
1620 throw new DataSetException("Record.saveWithoutStatusUpdate() is not yet implemented.");
1621 }
1622
1623 /***
1624 * Gets the schema for the parent DataSet
1625 *
1626 * @return the schema for the parent DataSet
1627 *
1628 * @throws DataSetException TODO: DOCUMENT ME!
1629 */
1630 public Schema schema()
1631 throws DataSetException
1632 {
1633 if (dataset() != null)
1634 {
1635 return this.schema;
1636 }
1637 else
1638 {
1639 throw new DataSetException("Internal Error: Record DataSet is null");
1640 }
1641 }
1642
1643 /***
1644 * Gets the DataSet for this Record
1645 *
1646 * @return the DataSet for this Record
1647 */
1648 public DataSet dataset()
1649 {
1650 return this.parentDataSet;
1651 }
1652
1653 /***
1654 * Sets the parent DataSet for this record.
1655 *
1656 * @param ds TODO: DOCUMENT ME!
1657 */
1658 void setParentDataSet(DataSet ds)
1659 {
1660 this.parentDataSet = ds;
1661 }
1662
1663 /***
1664 * return the value of each column as a string. Not yet implemented!
1665 *
1666 * @param valueseparator
1667 * @param maxwidths
1668 *
1669 * @return the formatted string
1670 *
1671 * @exception DataSetException
1672 */
1673 public String asFormattedString(String valueseparator, int [] maxwidths)
1674 throws DataSetException
1675 {
1676 throw new DataSetException("Not yet implemented!");
1677 }
1678
1679 /***
1680 * This returns a representation of this Record
1681 *
1682 * @return java.lang.String
1683 */
1684 public String toString()
1685 {
1686 try
1687 {
1688 ByteArrayOutputStream bout = new ByteArrayOutputStream();
1689 PrintWriter out = new PrintWriter(bout);
1690 out.print("{");
1691
1692 for (int i = 1; i <= size(); i++)
1693 {
1694 out.print("'" + getValue(i).asString() + "'");
1695
1696 if (i < size())
1697 {
1698 out.print(',');
1699 }
1700 }
1701
1702 out.print("}");
1703 out.flush();
1704
1705 return bout.toString();
1706 }
1707 catch (DataSetException e)
1708 {
1709 return "";
1710 }
1711 }
1712 }