Added enumerations ZOOM::Error and ZOOM::Event.
[ZOOM-Perl-moved-to-github.git] / lib / ZOOM.pm
1 # $Id: ZOOM.pm,v 1.5 2005-10-12 14:33:40 mike Exp $
2
3 use strict;
4 use warnings;
5 use Net::Z3950::ZOOM;
6
7
8 # Member naming convention: hash-element names which begin with an
9 # underscore represent underlying ZOOM-C object descriptors; those
10 # which lack them represent Perl's ZOOM objects.  (The same convention
11 # is used in naming local variables where appropriate.)
12 #
13 # So, for example, the ZOOM::Connection class has an {_conn} element,
14 # which is a pointer to the ZOOM-C Connection object; but the
15 # ZOOM::ResultSet class has a {conn} element, which is a reference to
16 # the Perl-level Connection object by which it was created.  (It may
17 # be that we find we have no need for these references, but for now
18 # they are retained.)
19 #
20 # To get at the underlying ZOOM-C connection object of a result-set
21 # (if you ever needed to do such a thing, which you probably don't)
22 # you'd use $rs->{conn}->_conn().
23
24 # ----------------------------------------------------------------------------
25
26 # The "Error" package contains constants returned as error-codes.
27 package ZOOM::Error;
28 sub NONE { Net::Z3950::ZOOM::ERROR_NONE }
29 sub CONNECT { Net::Z3950::ZOOM::ERROR_CONNECT }
30 sub MEMORY { Net::Z3950::ZOOM::ERROR_MEMORY }
31 sub ENCODE { Net::Z3950::ZOOM::ERROR_ENCODE }
32 sub DECODE { Net::Z3950::ZOOM::ERROR_DECODE }
33 sub CONNECTION_LOST { Net::Z3950::ZOOM::ERROR_CONNECTION_LOST }
34 sub INIT { Net::Z3950::ZOOM::ERROR_INIT }
35 sub INTERNAL { Net::Z3950::ZOOM::ERROR_INTERNAL }
36 sub TIMEOUT { Net::Z3950::ZOOM::ERROR_TIMEOUT }
37 sub UNSUPPORTED_PROTOCOL { Net::Z3950::ZOOM::ERROR_UNSUPPORTED_PROTOCOL }
38 sub UNSUPPORTED_QUERY { Net::Z3950::ZOOM::ERROR_UNSUPPORTED_QUERY }
39 sub INVALID_QUERY { Net::Z3950::ZOOM::ERROR_INVALID_QUERY }
40
41 # The "Event" package contains constants returned by last_event()
42 package ZOOM::Event;
43 sub NONE { Net::Z3950::ZOOM::EVENT_NONE }
44 sub CONNECT { Net::Z3950::ZOOM::EVENT_CONNECT }
45 sub SEND_DATA { Net::Z3950::ZOOM::EVENT_SEND_DATA }
46 sub RECV_DATA { Net::Z3950::ZOOM::EVENT_RECV_DATA }
47 sub TIMEOUT { Net::Z3950::ZOOM::EVENT_TIMEOUT }
48 sub UNKNOWN { Net::Z3950::ZOOM::EVENT_UNKNOWN }
49 sub SEND_APDU { Net::Z3950::ZOOM::EVENT_SEND_APDU }
50 sub RECV_APDU { Net::Z3950::ZOOM::EVENT_RECV_APDU }
51 sub RECV_RECORD { Net::Z3950::ZOOM::EVENT_RECV_RECORD }
52 sub RECV_SEARCH { Net::Z3950::ZOOM::EVENT_RECV_SEARCH }
53
54
55 # ----------------------------------------------------------------------------
56
57 package ZOOM::Exception;
58
59 sub new {
60     my $class = shift();
61     my($code, $message, $addinfo) = @_;
62
63     return bless {
64         code => $code,
65         message => $message,
66         addinfo => $addinfo,
67     }, $class;
68 }
69
70 sub code {
71     my $this = shift();
72     return $this->{code};
73 }
74
75 sub message {
76     my $this = shift();
77     return $this->{message};
78 }
79
80 sub addinfo {
81     my $this = shift();
82     return $this->{addinfo};
83 }
84
85
86 # ----------------------------------------------------------------------------
87
88 package ZOOM::Connection;
89
90 sub new {
91     my $class = shift();
92     my($host, $port) = @_;
93
94     my $_conn = Net::Z3950::ZOOM::connection_new($host, $port);
95     my($errcode, $errmsg, $addinfo) = (undef, "dummy", "dummy");
96     $errcode = Net::Z3950::ZOOM::connection_error($_conn, $errmsg, $addinfo);
97     die new ZOOM::Exception($errcode, $errmsg, $addinfo) if $errcode;
98
99     return bless {
100         host => $host,
101         port => $port,
102         _conn => $_conn,
103     };
104 }
105
106 # PRIVATE within this class
107 sub _conn {
108     my $this = shift();
109
110     my $_conn = $this->{_conn};
111     die "{_conn} undefined: has this ResultSet been destroy()ed?"
112         if !defined $_conn;
113
114     return $_conn;
115 }
116
117 sub option {
118     my $this = shift();
119     my($key, $value) = @_;
120
121     my $oldval = Net::Z3950::ZOOM::connection_option_get($this->_conn(), $key);
122     Net::Z3950::ZOOM::connection_option_set($this->_conn(), $key, $value)
123         if defined $value;
124
125     return $oldval;
126 }
127
128 sub search_pqf {
129     my $this = shift();
130     my($query) = @_;
131
132     my $_rs = Net::Z3950::ZOOM::connection_search_pqf($this->_conn(), $query);
133     my($errcode, $errmsg, $addinfo) = (undef, "dummy", "dummy");
134     $errcode = Net::Z3950::ZOOM::connection_error($this->_conn(),
135                                                   $errmsg, $addinfo);
136     die new ZOOM::Exception($errcode, $errmsg, $addinfo) if $errcode;
137
138     return _new ZOOM::ResultSet($this, $query, $_rs);
139 }
140
141 sub destroy {
142     my $this = shift();
143
144     Net::Z3950::ZOOM::connection_destroy($this->_conn());
145     $this->{_conn} = undef;
146 }
147
148
149 # ----------------------------------------------------------------------------
150
151 package ZOOM::ResultSet;
152
153 sub new {
154     my $class = shift();
155     die "You can't create $class objects directly";
156 }
157
158 # PRIVATE to ZOOM::Connection::search()
159 sub _new {
160     my $class = shift();
161     my($conn, $query, $_rs) = @_;
162
163     return bless {
164         conn => $conn,
165         query => $query,
166         _rs => $_rs,
167     }, $class;
168 }
169
170 # PRIVATE within this class
171 sub _rs {
172     my $this = shift();
173
174     my $_rs = $this->{_rs};
175     die "{_rs} undefined: has this ResultSet been destroy()ed?"
176         if !defined $_rs;
177
178     return $_rs;
179 }
180
181 sub size {
182     my $this = shift();
183
184     return Net::Z3950::ZOOM::resultset_size($this->_rs());
185 }
186
187 sub record {
188     my $this = shift();
189     my($which) = @_;
190
191     my $_rec = Net::Z3950::ZOOM::resultset_record($this->_rs(), $which);
192     ### Check for error -- but how?
193
194     # For some reason, I have to use the explicit "->" syntax in order
195     # to invoke the ZOOM::Record constructor here, even though I don't
196     # have to do the same for _new ZOOM::ResultSet above.  Weird.
197     return ZOOM::Record->_new($this, $which, $_rec);
198 }
199
200 sub destroy {
201     my $this = shift();
202
203     Net::Z3950::ZOOM::resultset_destroy($this->_rs());
204     $this->{_rs} = undef;
205 }
206
207
208 # ----------------------------------------------------------------------------
209
210 package ZOOM::Record;
211
212 sub new {
213     my $class = shift();
214     die "You can't create $class objects directly";
215 }
216
217 # PRIVATE to ZOOM::ResultSet::record()
218 sub _new {
219     my $class = shift();
220     my($rs, $which, $_rec) = @_;
221
222     return bless {
223         rs => $rs,
224         which => $which,
225         _rec => $_rec,
226     }, $class;
227 }
228
229 # PRIVATE within this class
230 sub _rec {
231     my $this = shift();
232
233     return $this->{_rec};
234 }
235
236 sub render {
237     my $this = shift();
238
239     my $len = 0;
240     my $string = Net::Z3950::ZOOM::record_get($this->_rec(), "render", $len);
241     # I don't think we need '$len' at all.  ### Probably the Perl-to-C
242     # glue code should use the value of `len' as well as the opaque
243     # data-pointer returned, to ensure that the SV contains all of the
244     # returned data and does not stop at the first NUL character in
245     # binary data.  Carefully check the ZOOM_record_get() documentation.
246     return $string;
247 }
248
249 sub raw {
250     my $this = shift();
251
252     my $len = 0;
253     my $string = Net::Z3950::ZOOM::record_get($this->_rec(), "raw", $len);
254     # See comment about $len in render()
255     return $string;
256 }
257
258
259 1;