Fix JVM segv on null pointers
[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     if (name == null)
68       throw new NullPointerException("option name cannot be null");
69     yaz4jlib.ZOOM_resultset_option_set(resultSet, name, value);
70     return this;
71   }
72
73   public Record getRecord(long index) throws ZoomException {
74     SWIGTYPE_p_ZOOM_record_p record =
75       yaz4jlib.ZOOM_resultset_record(resultSet, index);
76     //may be out of range or unsupported syntax
77     if (record == null) {
78       return null;
79     }
80     int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
81     if (errorCode != 0) {
82       throw new ZoomException("Record exception, code " + errorCode);
83     }
84     return new Record(record, this);
85   }
86   
87   /**
88    * Retrieve a collection of records at once. If a record cannot be retrieved,
89    * it is omitted from the list (thus the list size may be smaller than 'count').
90    * @param start start index within the result set
91    * @param count number of records to retrieve
92    * @return
93    * @throws ZoomException raised in case of protocol errors
94    */
95   public List<Record> getRecords(long start, int count) throws ZoomException {
96     List<Record> out = new ArrayList<Record>(count);
97     SWIGTYPE_p_p_ZOOM_record_p recs = yaz4jlib.new_zoomRecordArray(count);
98     yaz4jlib.ZOOM_resultset_records(resultSet, recs, start, count);
99     ZoomException err = this.conn.getZoomException();
100     if (err != null) { 
101       throw err;
102     }
103     for (int i = 0; i < count; i++) {
104       SWIGTYPE_p_ZOOM_record_p record =
105         yaz4jlib.zoomRecordArray_getitem(recs, i);
106       if (record == null) {
107         continue;
108       }
109       int errorCode = yaz4jlib.ZOOM_record_error(record, null, null, null);
110       if (errorCode != 0) {
111         throw new ZoomException("Record exception, code " + errorCode);
112       }
113       out.add(new Record(record, this));
114     }
115     return out;
116   }
117
118   @Override
119   public Iterator<Record> iterator() {
120     return new Iterator<Record>() {
121       private long cur;
122       @Override
123       public boolean hasNext() {
124         return cur < size;
125       }
126
127       @Override
128       public Record next() {
129         try {
130           return getRecord(cur++);
131         } catch (ZoomException ze) {
132           throw new NoSuchElementException(ze.getMessage());
133         }
134       }
135
136       @Override
137       public void remove() {
138         throw new UnsupportedOperationException("remove operation not supported");
139       }
140     };
141   }
142
143   /**
144    * 
145    * @param type
146    * @param spec
147    * @return
148    * @throws ZoomException 
149    */
150   public ResultSet sort(String type, String spec) throws ZoomException {
151     if (type == null)
152       throw new NullPointerException("sort type cannot be null");
153     if (spec == null)
154       throw new NullPointerException("sort spec cannot be null");
155     int ret = yaz4jlib.ZOOM_resultset_sort1(resultSet, type, spec);
156     if (ret != 0) throw new ZoomException("Sorting resultset failed");
157     return this;
158   }
159
160   public long getHitCount() {
161     return size;
162   }
163
164   void _dispose() {
165     if (!disposed) {
166       yaz4jlib.ZOOM_resultset_destroy(resultSet);
167       resultSet = null;
168       conn = null;
169       disposed = true;
170     }
171   }
172 }