View Javadoc

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