View Javadoc

1   package org.paneris.jal.model;
2   
3   import java.sql.Connection;
4   import java.sql.PreparedStatement;
5   import java.sql.ResultSet;
6   import java.util.Enumeration;
7   import java.util.Hashtable;
8   import java.util.Vector;
9   
10  import javax.servlet.http.HttpSession;
11  
12  import org.paneris.util.UniqueVector;
13  import org.webmacro.servlet.WebContext;
14  
15  /**
16   * 
17   * <p> A RecordSet provides functionality that allows you to navigate a set of results.</p>
18   * <p> It does this by storing itself in the session so that it can be navigated</p>
19   * <p> For this reason, the constructors are private, and you will be expected to
20   * always get the RecordsSet using getInstance();</p>
21   * <p> If you want to force the creation of a new record set, ensure that the context
22   * contains a "submit" form value.  (inplying that a new search has been run.)</p>
23   *
24   * usage example:
25   *
26   * RecordSet set = RecordSet.getInstance(context, db, table, sqlString, null);
27   * context.put("navigation", set);
28   * for (Enumeration enum = set.getNext(); enum.hasMoreElements();) {
29   *     Integer id = (Integer) enum.nextElement();
30   *     body.addElement(new DDRecord(db,table,id));
31   * }
32   *
33   */
34  
35  public class RecordSet {
36  
37    private UniqueVector recordIds;
38    protected static final boolean debug = false;
39    public String database;
40    private String table;
41    private int currentRecord;
42    public String sqlString;
43    public int increment;
44    private int max;
45    private TableMetaData metaData;
46    private boolean limited = false;
47    // hash of objects that can be used for storing state information associated with a record set
48    private Hashtable state = new Hashtable();
49    private Vector currentData = null;
50    public DataCache dataCache = DataCache.getInstance();
51    // allows records with a field 'deleted' to be ignored when loading the data
52    private boolean ignoreDeleted;
53  
54    /**
55     * Private Constructor to build a RecordSet given an sql string.
56     */
57    public RecordSet(String db, String table, String sql, Integer inc)
58      throws Exception {
59      init(db, table, inc);
60      DBConnectionManager connMgr = DBConnectionManager.getInstance();
61      sqlString = sql;
62      String runSQL = sqlString;
63      runSQL = metaData.applyMaxResults(database, runSQL);
64      if (debug)
65        System.err.println("sqlString: " + runSQL);
66      Connection conn = connMgr.getConnection("RecordSet", database);
67      PreparedStatement sqlStatement = conn.prepareStatement(runSQL);
68      buildSet(db, table, sqlStatement, inc);
69      connMgr.freeConnection(db, conn);
70    }
71  
72    /**
73     * Constructor to build a RecordSet given a vector of id's of results.
74     */
75    public RecordSet(String db, String table, UniqueVector results, Integer inc)
76      throws Exception {
77      init(db, table, inc);
78      recordIds = results;
79    }
80  
81    /**
82     * Private Constructor to build a RecordSet given a prepared statement
83     * nb this method does not set a max limit of the records returned.
84     */
85    public RecordSet(
86      String db,
87      String table,
88      PreparedStatement sqlStatement,
89      Integer inc)
90      throws Exception {
91      init(db, table, inc);
92      buildSet(db, table, sqlStatement, inc);
93    }
94  
95    public void buildSet(
96      String db,
97      String table,
98      PreparedStatement sqlStatement,
99      Integer inc)
100     throws Exception {
101     recordIds = new UniqueVector();
102     ResultSet rs = sqlStatement.executeQuery();
103     while (rs.next()) {
104       Integer id;
105       // eithier get the id, or the 1st field.
106       try {
107         id = new Integer(rs.getInt("id"));
108       }
109       catch (Exception e) {
110         id = new Integer(rs.getInt(1));
111       }
112       recordIds.addElement(id);
113     }
114     if (recordIds.size() == metaData.getMaxResults()) {
115       limited = true;
116     }
117   }
118 
119   /**
120    * set initial values for this record set
121    */
122   private void init(String db, String t, Integer inc) throws Exception {
123     currentRecord = 0;
124     database = db;
125     table = t;
126     DataCache dataCache = DataCache.getInstance();
127     metaData = dataCache.getTableMetaData(db, table);
128     if (inc == null) {
129       increment = metaData.getResultsPerPage().intValue();
130     }
131     else {
132       increment = inc.intValue();
133     }
134   }
135 
136   /**
137    * Returns the single instance, creating one if it can't be found (or has changed)
138    * use this method if you have pre-built your results (ie a simple sql string does not
139    * provide enough flexability).
140    *
141    * set the increment to null, in order to have it defaulted for this table
142    */
143   public static synchronized RecordSet getInstance(
144     WebContext context,
145     String db,
146     String table,
147     UniqueVector results,
148     Integer inc)
149     throws Exception {
150     HttpSession session = context.getSession();
151     RecordSet instance = (RecordSet)session.getAttribute(table);
152     if (instance == null || (context.getForm("submit") != null)) {
153       instance = new RecordSet(db, table, results, inc);
154       session.setAttribute(table, instance);
155       if (debug)
156         System.err.println("new instance");
157     }
158     else {
159       instance.doNavigation(context);
160     }
161     return instance;
162   }
163 
164   /**
165    * Returns the single instance, creating one if it can't be found (or has changed)
166    *
167    * set the increment to null, in order to have it defaulted for this table
168    * the RecordSet is referenced in the session by the identifier
169    */
170   public static synchronized RecordSet getInstance(
171     WebContext context,
172     String db,
173     String table,
174     String sql,
175     Integer inc,
176     String identifier)
177     throws Exception {
178     HttpSession session = context.getSession();
179     RecordSet instance = (RecordSet)session.getAttribute(identifier);
180     if (instance == null
181       || context.getForm("submit") != null
182       || !sql.equals(instance.sqlString)
183       || (inc != null && (inc.intValue() != instance.increment))) {
184       instance = new RecordSet(db, table, sql, inc);
185       session.setAttribute(identifier, instance);
186     }
187     else {
188       instance.doNavigation(context);
189     }
190     return instance;
191   }
192 
193   /**
194    * Returns the single instance, creating one if it can't be found (or has changed)
195    *
196    * set the increment to null, in order to have it defaulted for this table
197    * the RecordSet is referenced in the session by the identifier
198    */
199   public static synchronized RecordSet getInstance(
200     WebContext context,
201     String db,
202     String table,
203     PreparedStatement sql,
204     Integer inc,
205     String identifier)
206     throws Exception {
207     HttpSession session = context.getSession();
208     RecordSet instance = (RecordSet)session.getAttribute(identifier);
209     if (instance == null
210       || context.getForm("submit") != null
211       || (inc != null && (inc.intValue() != instance.increment))) {
212       instance = new RecordSet(db, table, sql, inc);
213       session.setAttribute(identifier, instance);
214     }
215     else {
216       instance.doNavigation(context);
217     }
218     return instance;
219   }
220 
221   /**
222    * copes with situations where no identifier is specified (should be marked as depreciated)
223    */
224   public static synchronized RecordSet getInstance(
225     WebContext context,
226     String db,
227     String table,
228     String sql,
229     Integer inc)
230     throws Exception {
231     return getInstance(context, db, table, sql, inc, table);
232   }
233 
234   /**
235    * set the ignore deleted flag
236    */
237   public void setIgnoreDeleted(boolean del) {
238     ignoreDeleted = true;
239   }
240 
241   /**
242    * Returns the single instance, or null it can't be found (or has changed)
243    *
244    * the RecordSet is referenced in the session by the identifier
245    */
246   public static synchronized RecordSet getInstance(
247     WebContext context,
248     String identifier)
249     throws Exception {
250     HttpSession session = context.getSession();
251     RecordSet instance = (RecordSet)session.getAttribute(identifier);
252     if (instance == null || (context.getForm("submit") != null)) {
253       session.removeAttribute(identifier);
254       return null;
255     }
256     else {
257       instance.doNavigation(context);
258       return instance;
259     }
260   }
261 
262   public void doNavigation(WebContext context) throws Exception {
263     if (context.getForm("next") != null) {
264       moveNext();
265     }
266     if (context.getForm("previous") != null) {
267       movePrevious();
268     }
269   }
270 
271   // get the next x records - returns an enumeration so that you can build your own results
272   public Enumeration getNext() throws Exception {
273     Vector results = new Vector();
274     increment();
275     for (int i = currentRecord; i < max; i++) {
276       Integer id = (Integer)recordIds.elementAt(i);
277       results.addElement(id);
278     }
279     return results.elements();
280   }
281 
282   // get the next x records - returned as a vector of ddrecords, useful for very simple handlets
283   public Vector getRows() throws Exception {
284     loadData();
285     return currentData;
286   }
287 
288   // get the next x records - returned as a vector of ddrecords, useful for very simple handlets
289   public void loadData() throws Exception {
290     Vector results = new Vector();
291     increment();
292     for (int i = currentRecord; i < max; i++) {
293       Integer id = null;
294       // this is required in order to cope with situations when a record has 
295       // been deleted
296       try {
297         id = (Integer)recordIds.elementAt(i);
298       }
299       catch (ArrayIndexOutOfBoundsException e) {
300         ; // if record has been deleted
301       }
302       if (id != null) {
303         DDRecord ddtable = getRecord(database, table, id);
304         DDField field = (DDField)ddtable.get("id");
305         // ignore deleted records?
306         boolean del = false;
307         if (ignoreDeleted) {
308           try {
309             DDField deleted = (DDField)ddtable.get("deleted");
310             del = ((Boolean)deleted.getValue()).booleanValue();
311           }
312           catch (NoSuchFieldException e) { 
313             ; // it might not be there
314           }
315         }
316         if (field.getValue() == null || del) {
317           recordIds.removeElementAt(i);
318           i--;
319         }
320         else {
321           results.addElement(ddtable);
322         }
323       }
324     }
325     setMaxRecord(max);
326     currentData = results;
327   }
328 
329   // split out this method so that it can be overwritten
330   public DDRecord getRecord(String database, String table, Integer id)
331     throws Exception {
332     return dataCache.getDDRecord(database, table, id);
333   }
334 
335   // remove a record for the table
336   public boolean removeID(Integer id) {
337     return recordIds.removeElement(id);
338   }
339 
340   private void increment() {
341     if (increment == 0) {
342       max = recordIds.size();
343     }
344     else {
345       max = currentRecord + increment;
346     }
347     setMaxRecord(max);
348   }
349 
350   // allows records to be dynamically inserted into the recordset
351   public void addElement(Integer id) {
352     recordIds.insertElementAt(id, currentRecord);
353   }
354 
355   public TableMetaData getMetaData() {
356     return metaData;
357   }
358 
359   public int getIncrement() {
360     return increment;
361   }
362 
363   public boolean getLimited() {
364     return limited;
365   }
366 
367   public int getSize() {
368     return recordIds.size();
369   }
370 
371   public int getCurrentRecord() {
372     return currentRecord;
373   }
374 
375   public void setCurrentRecord(int i) {
376     currentRecord = i;
377   }
378 
379   public int getDisplayCurrentRecord() {
380     if (getSize() > 0) {
381       return currentRecord + 1;
382     }
383     else {
384       return 0;
385     }
386   }
387 
388   public int getMaxRecord() {
389     return max;
390   }
391 
392   public void setMaxRecord(int a) {
393     max = a;
394     if (max > recordIds.size()) {
395       max = recordIds.size();
396     }
397   }
398 
399   public int getPages() {
400     if (increment == 0) {
401       return 1;
402     }
403     else {
404       if ((recordIds.size() % increment) == 0) {
405         return (recordIds.size() / increment);
406       }
407       else {
408         return (recordIds.size() / increment) + 1;
409       }
410     }
411   }
412 
413   public int getPage() {
414     if (increment == 0) {
415       return 1;
416     }
417     else {
418       return ((currentRecord) / increment) + 1;
419     }
420   }
421 
422   public String getSqlString() {
423     return sqlString;
424   }
425 
426   public void moveNext() {
427     currentRecord += increment;
428   }
429 
430   public void movePrevious() {
431     currentRecord -= increment;
432   }
433 
434   public boolean getStart() {
435     if (getPage() == 1) {
436       return true;
437     }
438     else {
439       return false;
440     }
441   }
442 
443   public boolean getEnd() {
444     if (getPage() == getPages()) {
445       return true;
446     }
447     else {
448       return false;
449     }
450   }
451 
452   public Hashtable getState() {
453     return state;
454   }
455 
456 }