Move async result cursor to AsyncConnection
[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   private Connection conn;
35   SWIGTYPE_p_ZOOM_resultset_p resultSet;
36   private boolean disposed = false;
37
38   ResultSet(SWIGTYPE_p_ZOOM_resultset_p resultSet, Connection conn) {
39     //do not copy anything to the java side at this point, it won't be valid
40     //in the async mode
41     this.resultSet = resultSet;
42     this.conn = conn;
43   }
44
45   @Override
46   public void finalize() {
47     this._dispose();
48   }
49
50   /**
51    * Read option by name.
52    * @param name option name
53    * @return option value
54    */
55   public String option(String name) {
56     return yaz4jlib.ZOOM_resultset_option_get(resultSet, name);
57   }
58
59   /**
60    * Write option with a given name.
61    * @param name option name
62    * @param value option value
63    * @return result set (self) for chainability
64    */
65   public ResultSet option(String name, String value) {
66     if (name == null)
67       throw new NullPointerException("option name cannot be null");
68     yaz4jlib.ZOOM_resultset_option_set(resultSet, name, value);
69     return this;
70   }
71
72   public Record getRecord(long index) throws ZoomException {
73     SWIGTYPE_p_ZOOM_record_p record =
74       yaz4jlib.ZOOM_resultset_record(resultSet, index);
75     //may be out of range or unsupported syntax
76     if (record == null) {
77       return null;
78     }
79     int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
80     if (errorCode != 0) {
81       throw new ZoomException("Record exception, code " + errorCode);
82     }
83     return new Record(record, this);
84   }
85   
86   /**
87    * Retrieve a collection of records at once. If a record cannot be retrieved,
88    * it is omitted from the list (thus the list size may be smaller than 'count').
89    * @param start start index within the result set
90    * @param count number of records to retrieve
91    * @return
92    * @throws ZoomException raised in case of protocol errors
93    */
94   public List<Record> getRecords(long start, int count) throws ZoomException {
95     List<Record> out = new ArrayList<Record>(count);
96     SWIGTYPE_p_p_ZOOM_record_p recs = yaz4jlib.new_zoomRecordArray(count);
97     yaz4jlib.ZOOM_resultset_records(resultSet, recs, start, count);
98     ZoomException err = this.conn.getZoomException();
99     if (err != null) { 
100       throw err;
101     }
102     for (int i = 0; i < count; i++) {
103       SWIGTYPE_p_ZOOM_record_p record =
104         yaz4jlib.zoomRecordArray_getitem(recs, i);
105       if (record == null) {
106         continue;
107       }
108       int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
109       if (errorCode != 0) {
110         throw new ZoomException("Record exception, code " + errorCode);
111       }
112       out.add(new Record(record, this));
113     }
114     return out;
115   }
116
117   @Override
118   public Iterator<Record> iterator() {
119     return new Iterator<Record>() {
120       private long cur;
121       @Override
122       public boolean hasNext() {
123         return cur < getHitCount();
124       }
125
126       @Override
127       public Record next() {
128         try {
129           return getRecord(cur++);
130         } catch (ZoomException ze) {
131           throw new NoSuchElementException(ze.getMessage());
132         }
133       }
134
135       @Override
136       public void remove() {
137         throw new UnsupportedOperationException("remove operation not supported");
138       }
139     };
140   }
141
142   /**
143    * 
144    * @param type
145    * @param spec
146    * @return
147    * @throws ZoomException 
148    */
149   public ResultSet sort(String type, String spec) throws ZoomException {
150     if (type == null)
151       throw new NullPointerException("sort type cannot be null");
152     if (spec == null)
153       throw new NullPointerException("sort spec cannot be null");
154     int ret = yaz4jlib.ZOOM_resultset_sort1(resultSet, type, spec);
155     if (ret != 0) throw new ZoomException("Sorting resultset failed");
156     return this;
157   }
158
159   public long getHitCount() {
160     return yaz4jlib.ZOOM_resultset_size(this.resultSet);
161   }
162
163   void _dispose() {
164     if (!disposed) {
165       yaz4jlib.ZOOM_resultset_destroy(resultSet);
166       resultSet = null;
167       conn = null;
168       disposed = true;
169     }
170   }
171 }