Fix error handling in ResultSet#getRecords
[yaz4j-moved-to-github.git] / src / main / java / org / yaz4j / ResultSet.java
1 package org.yaz4j;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.List;
6 import java.util.NoSuchElementException;
7 import org.yaz4j.exception.ZoomException;
8 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_record_p;
9 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_resultset_p;
10 import org.yaz4j.jni.SWIGTYPE_p_p_ZOOM_record_p;
11 import org.yaz4j.jni.yaz4jlib;
12
13 /**
14  * This class represents a "buffered handle" to the result set created on the
15  * server and thus retrieving records may involve a request to the server if
16  * those records are not locally cached. Details on how to configure the retrieval
17  * (present) process are available in the YAZ manual
18  *
19  * @see <a href="http://www.indexdata.com/yaz/doc/zoom.resultsets.html">YAZ ZOOM result sets</a>
20  *
21  * Because of server misbehavior or errors during retrieval the
22  * "getRecord" method may either return null or throw exceptions, even when the
23  * index of retrieved records lies within the bounds of the set. Client
24  * code should be prepared for such situations.
25  *
26  * This class implements the iterable interface and as such can be used within
27  * foreach loops, it's important to note, however, that in this case the errors
28  * during retrieval will be masked with standard NoSuchElementExceptions.
29  *
30  * @author jakub
31  */
32 public class ResultSet implements Iterable<Record> {
33   //for GC refcount
34
35   private Connection conn;
36   private SWIGTYPE_p_ZOOM_resultset_p resultSet;
37   private long size = 0;
38   private boolean disposed = false;
39
40   ResultSet(SWIGTYPE_p_ZOOM_resultset_p resultSet, Connection conn) {
41     this.resultSet = resultSet;
42     size = yaz4jlib.ZOOM_resultset_size(this.resultSet);
43     this.conn = conn;
44   }
45
46   @Override
47   public void finalize() {
48     this._dispose();
49   }
50
51   /**
52    * Read option by name.
53    * @param name option name
54    * @return option value
55    */
56   public String option(String name) {
57     return yaz4jlib.ZOOM_resultset_option_get(resultSet, name);
58   }
59
60   /**
61    * Write option with a given name.
62    * @param name option name
63    * @param value option value
64    * @return result set (self) for chainability
65    */
66   public ResultSet option(String name, String value) {
67     yaz4jlib.ZOOM_resultset_option_set(resultSet, name, value);
68     return this;
69   }
70
71   public Record getRecord(long index) throws ZoomException {
72     SWIGTYPE_p_ZOOM_record_p record =
73       yaz4jlib.ZOOM_resultset_record(resultSet, index);
74     //may be out of range or unsupported syntax
75     if (record == null) {
76       return null;
77     }
78     int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
79     if (errorCode != 0) {
80       throw new ZoomException("Record excpetion, code " + errorCode);
81     }
82     return new Record(record, this);
83   }
84   
85   /**
86    * Retrieve a collection of records at once. If a record cannot be retrieved,
87    * it is omitted from the list (thus the list size may be smaller than 'count').
88    * @param start start index within the result set
89    * @param count number of records to retrieve
90    * @return
91    * @throws ZoomException raised in case of protocol errors
92    */
93   public List<Record> getRecords(long start, int count) throws ZoomException {
94     List<Record> out = new ArrayList<Record>(count);
95     SWIGTYPE_p_p_ZOOM_record_p recs = yaz4jlib.new_zoomRecordArray(count);
96     yaz4jlib.ZOOM_resultset_records(resultSet, recs, start, count);
97     ZoomException err = this.conn.getZoomException();
98     if (err != null) { 
99       throw err;
100     }
101     for (int i = 0; i < count; i++) {
102       SWIGTYPE_p_ZOOM_record_p record =
103         yaz4jlib.zoomRecordArray_getitem(recs, i);
104       if (record == null) {
105         continue;
106       }
107       int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
108       if (errorCode != 0) {
109         throw new ZoomException("Record excpetion, code " + errorCode);
110       }
111       out.add(new Record(record, this));
112     }
113     return out;
114   }
115
116   @Override
117   public Iterator<Record> iterator() {
118     return new Iterator<Record>() {
119       private long cur;
120       @Override
121       public boolean hasNext() {
122         return cur < size;
123       }
124
125       @Override
126       public Record next() {
127         try {
128           return getRecord(cur++);
129         } catch (ZoomException ze) {
130           throw new NoSuchElementException(ze.getMessage());
131         }
132       }
133
134       @Override
135       public void remove() {
136         throw new UnsupportedOperationException("remove operation not supported");
137       }
138     };
139   }
140
141   /**
142    * 
143    * @param type
144    * @param spec
145    * @return
146    * @throws ZoomException 
147    */
148   public ResultSet sort(String type, String spec) throws ZoomException {
149     int ret = yaz4jlib.ZOOM_resultset_sort1(resultSet, type, spec);
150     if (ret != 0) throw new ZoomException("Sorting resultset failed");
151     return this;
152   }
153
154   public long getHitCount() {
155     return size;
156   }
157
158   void _dispose() {
159     if (!disposed) {
160       yaz4jlib.ZOOM_resultset_destroy(resultSet);
161       resultSet = null;
162       conn = null;
163       disposed = true;
164     }
165   }
166 }