Updated copyright headers.
[yazpp-moved-to-github.git] / doc / zoom.xml
1 <chapter id="zoom">
2  <title>ZOOM-C++</title>
3
4
5  <sect1 id="zoom-introduction">
6   <title>Introduction</title>
7   <para>
8    <ulink url="&url.zoom;">ZOOM</ulink>
9    is the emerging standard API for information retrieval programming
10    using the Z39.50 protocol.  ZOOM's
11    <ulink url="&url.zoom.api;">Abstract API</ulink>
12    specifies semantics for classes representing key IR concepts such as
13    connections, queries, result sets and records; and there are various
14    <ulink url="&url.zoom.bind;">bindings</ulink>
15    specifying how those concepts should be represented in various
16    programming languages.
17   </para>
18   <para>
19    The YAZ++ library includes an implementation of the <ulink
20    url="&url.zoom.bind.cplusplus;">C++ binding</ulink>
21    for ZOOM, enabling quick, easy development of client applications.
22   </para>
23   <para>
24    For example, here is a tiny Z39.50 client that fetches and displays
25    the MARC record for Farlow &amp; Brett Surman's
26    <citetitle>The Complete Dinosaur</citetitle>
27    from the Library of Congress's Z39.50 server:
28   </para>
29   <programlisting>
30     #include &lt;iostream&gt;
31     #include &lt;yazpp/zoom.h&gt;
32
33     using namespace ZOOM;
34
35     int main(int argc, char **argv)
36     {
37         connection conn("z3950.loc.gov", 7090);
38         conn.option("databaseName", "Voyager");
39         conn.option("preferredRecordSyntax", "USMARC");
40         resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
41         const record *rec = rs.getRecord(0);
42         cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
43     }
44   </programlisting>
45    <note>
46     <para>
47      For the sake of simplicity, this program does not check
48      for errors: we show a more robust version of the same program
49      <link linkend="revised-sample">later</link>.)
50     </para>
51    </note>
52   <para>
53    YAZ++'s implementation of the C++ binding is a thin layer over YAZ's
54    implementation of the C binding.  For information on the supported
55    options and other such details, see the ZOOM-C documentation, which
56    can be found on-line at
57    <ulink url="&url.yaz.zoom;"/>
58   </para>
59   <para>
60    All of the classes defined by ZOOM-C++ are in the
61    <literal>ZOOM</literal> namespace.  We will now consider
62    the five main classes in turn:
63
64    <itemizedlist>
65     <listitem>
66      <para>
67       <literal>connection</literal>
68      </para>
69     </listitem>
70
71     <listitem>
72      <para>
73       <literal>query</literal> and its subclasses
74       <literal>prefixQuery</literal> and
75       <literal>CCLQuery</literal>
76      </para>
77     </listitem>
78
79     <listitem>
80      <para>
81       <literal>resultSet</literal>
82      </para>
83     </listitem>
84
85     <listitem>
86      <para>
87       <literal>record</literal>
88      </para>
89     </listitem>
90
91     <listitem>
92      <para>
93       <literal>exception</literal> and its subclasses
94       <literal>systemException</literal>,
95       <literal>bib1Exception</literal> and
96       <literal>queryException</literal>
97      </para>
98     </listitem>
99    </itemizedlist>
100   </para>
101  </sect1>
102
103
104  <sect1 id="zoom-connection">
105   <title><literal>ZOOM::connection</literal></title>
106   <para>
107    A <literal>ZOOM::connection</literal> object represents an open
108    connection to a Z39.50 server.  Such a connection is forged by
109    constructing a <literal>connection</literal> object.
110   </para>
111   <para>
112    The class has this declaration:
113   </para>
114   <synopsis>
115     class connection {
116     public:
117       connection (const char *hostname, int portnum);
118       ~connection ();
119       const char *option (const char *key) const;
120       const char *option (const char *key, const char *val);
121     };
122   </synopsis>
123   <para>
124    When a new <literal>connection</literal> is created, the hostname
125    and port number of a Z39.50 server must be supplied, and the
126    network connection is forged and wrapped in the new object.  If the
127    connection can't be established - perhaps because the hostname
128    couldn't be resolved, or there is no server listening on the
129    specified port - then an
130    <link linkend="zoom-exception"><literal>exception</literal></link>
131    is thrown.
132   </para>
133   <para>
134    The only other methods on a <literal>connection</literal> object
135    are for getting and setting options.  Any name-value pair of
136    strings may be set as options, and subsequently retrieved, but
137    certain options have special meanings which are understood by the
138    ZOOM code and affect the behaviour of the object that carries
139    them.  For example, the value of the
140    <literal>databaseName</literal> option is used as the name of the
141    database to query when a search is executed against the
142    <literal>connection</literal>.  For a full list of such special
143    options, see the ZOOM abstract API and the ZOOM-C documentation
144    (links below).
145   </para>
146
147   <sect2 id="connection.references">
148    <title>References</title>
149    <itemizedlist>
150     <listitem>
151      <para>
152       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.2"
153         >Section 3.2 (Connection) of the ZOOM Abstract API</ulink>
154       </para>
155      </listitem>
156      <listitem>
157       <para>
158        <ulink url="&url.yaz.zoom.connections;"
159         >The Connections section f the ZOOM-C documentation</ulink>
160       </para>
161      </listitem>
162     </itemizedlist>
163    </sect2>
164   </sect1>
165
166
167  <sect1 id="zoom-query">
168   <title><literal>ZOOM::query</literal> and subclasses</title>
169   <para>
170    The <literal>ZOOM::query</literal> class is a virtual base class,
171    representing a query to be submitted to a server.  This class has
172    no methods, but two (so far) concrete subclasses, each implementing
173    a specific query notation.
174   </para>
175
176   <sect2 id="ZOOM::prefixQuery">
177    <title><literal>ZOOM::prefixQuery</literal></title>
178    <synopsis>
179     class prefixQuery : public query {
180     public:
181       prefixQuery (const char *pqn);
182       ~prefixQuery ();
183     };
184    </synopsis>
185     <para>
186      This class enables a query to be created by compiling YAZ's
187      cryptic but powerful
188      <ulink url="&url.yaz.pqf;">Prefix Query Notation (PQN)</ulink>.
189     </para>
190    </sect2>
191    
192   <sect2 id="ZOOM::CCLQuery">
193    <title><literal>ZOOM::CCLQuery</literal></title>
194    <synopsis>
195     class CCLQuery : public query {
196     public:
197       CCLQuery (const char *ccl, void *qualset);
198       ~CCLQuery ();
199     };
200    </synopsis>
201    <para>
202     This class enables a query to be created using the simpler but
203     less expressive
204     <ulink url="&url.yaz.ccl;">Common Command Language (CCL)</ulink>.
205     The qualifiers recognised by the CCL parser are specified in an
206     external configuration file in the format described by the YAZ
207     documentation.
208    </para>
209    <para>
210     If query construction fails for either type of
211     <literal>query</literal> object - typically because the query
212     string itself is not valid PQN or CCL - then an
213     <link linkend="zoom-exception"><literal>exception</literal></link>
214     is thrown.
215    </para>
216   </sect2>
217
218   <sect2 id="queries.discussion">
219    <title>Discussion</title>
220    <para>
221     It will be readily recognised that these objects have no methods
222     other than their constructors: their only role in life is to be
223     used in searching, by being passed to the
224     <literal>resultSet</literal> class's constructor.
225    </para>
226    <para>
227     Given a suitable set of CCL qualifiers, the following pairs of
228     queries are equivalent:
229    </para>
230    <screen>
231     prefixQuery("dinosaur");
232     CCLQuery("dinosaur");
233
234     prefixQuery("@and complete dinosaur");
235     CCLQuery("complete and dinosaur");
236
237     prefixQuery("@and complete @or dinosaur pterosaur");
238     CCLQuery("complete and (dinosaur or pterosaur)");
239
240     prefixQuery("@attr 1=7 0253333490");
241     CCLQuery("isbn=0253333490");
242    </screen>
243   </sect2>
244
245   <sect2 id="query.references">
246    <title>References</title>
247    <itemizedlist>
248     <listitem>
249      <para>
250       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.3"
251         >Section 3.3 (Query) of the ZOOM Abstract API</ulink>
252      </para>
253     </listitem>
254     <listitem>
255      <para>
256        <ulink url="&url.yaz.zoom.query;"
257         >The Queries section of the ZOOM-C documentation</ulink>
258      </para>
259     </listitem>
260    </itemizedlist>
261   </sect2>
262  </sect1>
263
264
265  <sect1 id="zoom-resultset">
266   <title><literal>ZOOM::resultSet</literal></title>
267   <para>
268    A <literal>ZOOM::resultSet</literal> object represents a set of
269    records identified by a query that has been executed against a
270    particular connection.  The sole purpose of both
271    <literal>connection</literal> and <literal>query</literal> objects
272    is that they can be used to create new
273    <literal>resultSet</literal>s - that is, to perform a search on the
274    server on the remote end of the connection.
275   </para>
276   <para>
277    The class has this declaration:
278   </para>
279   <synopsis>
280     class resultSet {
281     public:
282       resultSet (connection &amp;c, const query &amp;q);
283       ~resultSet ();
284       const char *option (const char *key) const;
285       const char *option (const char *key, const char *val);
286       size_t size () const;
287       const record *getRecord (size_t i) const;
288     };
289   </synopsis>
290   <para>
291    New <literal>resultSet</literal>s are created by the constructor,
292    which is passed a <literal>connection</literal>, indicating the
293    server on which the search is to be performed, and a
294    <literal>query</literal>, indicating what search to perform.  If
295    the search fails - for example, because the query uses attributes
296    that the server doesn't implement - then an
297    <link linkend="zoom-exception"><literal>exception</literal></link>
298    is thrown.
299   </para>
300   <para>
301    Like <literal>connection</literal>s, <literal>resultSet</literal>
302    objects can carry name-value options.  The special options which
303    affect ZOOM-C++'s behaviour are the same as those for ZOOM-C and
304    are described in its documentation (link below).  In particular,
305    the <literal>preferredRecordSyntax</literal> option may be set to
306    a string such as ``USMARC'', ``SUTRS'' etc. to indicate what the
307    format in which records should be retrieved; and the
308    <literal>elementSetName</literal> option indicates whether brief
309    records (``B''), full records (``F'') or some other composition
310    should be used.
311   </para>
312   <para>
313    The <literal>size()</literal> method returns the number of records
314    in the result set.  Zero is a legitimate value: a search that finds
315    no records is not the same as a search that fails.
316   </para>
317   <para>
318    Finally, the <literal>getRecord</literal> method returns the
319    <parameter>i</parameter>th record from the result set, where
320    <parameter>i</parameter> is zero-based: that is, legitmate values
321    range from zero up to one less than the result-set size.  If the
322    method fails, for example because the requested record is out of
323    range, it <literal>throw</literal>s an
324    <link linkend="zoom-exception"><literal>exception</literal></link>.
325   </para>
326
327   <sect2 id="resultset.references">
328    <title>References</title>
329    <itemizedlist>
330     <listitem>
331      <para>
332       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.4"
333         >Section 3.4 (Result Set) of the ZOOM Abstract API</ulink>
334      </para>
335     </listitem>
336     <listitem>
337      <para>
338       <ulink url="&url.yaz.zoom.resultsets;"
339         >The Result Sets section of the ZOOM-C documentation</ulink>
340      </para>
341     </listitem>
342    </itemizedlist>
343   </sect2>
344  </sect1>
345
346
347  <sect1 id="zoom-record">
348   <title><literal>ZOOM::record</literal></title>
349   <para>
350    A <literal>ZOOM::record</literal> object represents a chunk of data
351    from a <literal>resultSet</literal> returned from a server.
352   </para>
353   <para>
354    The class has this declaration:
355   </para>
356   <synopsis>
357     class record {
358     public:
359       ~record ();
360       enum syntax {
361         UNKNOWN, GRS1, SUTRS, USMARC, UKMARC, XML
362       };
363       record *clone () const;
364       syntax recsyn () const;
365       const char *render () const;
366       const char *rawdata () const;
367     };
368   </synopsis>
369   <para>
370    Records returned from Z39.50 servers are encoded using a record
371    syntax: the various national MARC formats are commonly used for
372    bibliographic data, GRS-1 or XML for complex structured data, SUTRS
373    for simple human-readable text, etc.  The
374    <literal>record::syntax</literal> enumeration specifies constants
375    representing common record syntaxes, and the
376    <literal>recsyn()</literal> method returns the value corresponding
377    to the record-syntax of the record on which it is invoked.
378    <note>
379     <para>
380      Because this interface uses an enumeration, it is difficult to
381      extend to other record syntaxes - for example, DANMARC, the MARC
382      variant widely used in Denmark.  We might either grow the
383      enumeration substantially, or change the interface to return
384      either an integer or a string.
385     </para>
386    </note>
387   </para>
388   <para>
389    The simplest thing to do with a retrieved record is simply to
390    <literal>render()</literal> it.  This returns a human-readable, but
391    not necessarily very pretty, representation of the contents of the
392    record.  This is useful primarily for testing and debugging, since
393    the application has no control over how the record appears.
394    (The application must <emphasis>not</emphasis>
395    <literal>delete</literal> the returned string - it is ``owned'' by
396    the record object.)
397   </para>
398   <para>
399    More sophisticated applications will want to deal with the raw data
400    themselves: the <literal>rawdata()</literal> method returns it.
401    Its format will vary depending on the record syntax: SUTRS, MARC
402    and XML records are returned ``as is'', and GRS-1 records as a
403    pointer to their top-level node, which is a
404    <literal>Z_GenericRecord</literal> structure as defined in the
405    <literal>&lt;yaz/z-grs.h&gt;</literal> header file.
406    (The application must <emphasis>not</emphasis>
407    <literal>delete</literal> the returned data - it is ``owned'' by
408    the record object.)
409   </para>
410   <para>
411    Perceptive readers will notice that there are no methods for access
412    to individual fields within a record.  That's because the different
413    record syntaxes are so different that there is no even a uniform
414    notion of what a field is across them all, let alone a sensible way
415    to implement such a function.  Fetch the raw data instead, and pick
416    it apart ``by hand''.
417   </para>
418
419   <sect2 id="zoom.memory.management">
420    <title>Memory Management</title>
421    <para>
422     The <literal>record</literal> objects returned from
423     <literal>resultSet::getRecord()</literal> are ``owned'' by the
424     result set object: that means that the application is not
425     responsible for <literal>delete</literal>ing them - each
426     <literal>record</literal> is automatically deallocated when the
427     <literal>resultSet</literal> that owns it is
428     <literal>delete</literal>d.
429    </para>
430    <para>
431     Usually that's what you want: it means that you can easily fetch a
432     record, use it and forget all about it, like this:
433    </para>
434    <programlisting>
435     resultSet rs(conn, query);
436     cout &lt;&lt; rs.getRecord(0)-&gt;render();
437    </programlisting>
438    <para>
439     But sometimes you want a <literal>record</literal> to live on past
440     the lifetime of the <literal>resultSet</literal> from which it was
441     fetched.  In this case, the <literal>clone(f)</literal> method can
442     be used to make an autonomous copy.  The application must
443     <literal>delete</literal> it when it doesn't need it any longer:
444    </para>
445    <programlisting>
446     record *rec;
447     {
448         resultSet rs(conn, query);
449         rec = rs.getRecord(0)-&gt;clone();
450         // `rs' goes out of scope here, and is deleted
451     }
452     cout &lt;&lt; rec-&gt;render();
453     delete rec;
454    </programlisting>
455   </sect2>
456
457   <sect2 id="record.references">
458    <title>References</title>
459    <itemizedlist>
460     <listitem>
461      <para>
462       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.5"
463         >Section 3.5 (Record) of the ZOOM Abstract API</ulink>
464      </para>
465     </listitem>
466     <listitem>
467      <para>
468       <ulink url="&url.yaz.zoom.records;"
469         >The Records section of the ZOOM-C documentation</ulink>
470      </para>
471     </listitem>
472    </itemizedlist>
473   </sect2>
474  </sect1>
475
476
477  <sect1 id="zoom-exception">
478   <title><literal>ZOOM::exception</literal> and subclasses</title>
479   <para>
480    The <literal>ZOOM::exception</literal> class is a virtual base
481    class, representing a diagnostic generated by the ZOOM-C++ library
482    or returned from a server.  Its subclasses represent particular
483    kinds of error.
484   </para>
485   <para>
486    When any of the ZOOM methods fail, they respond by
487    <literal>throw</literal>ing an object of type
488    <literal>exception</literal> or one of its subclasses.  This most
489    usually happens with the <literal>connection</literal> constructor,
490    the various query constructors, the <literal>resultSet</literal>
491    constructor (which is actually the searching method) and
492    <literal>resultSet::getRecord()</literal>.
493   </para>
494   <para>
495     The base class has this declaration:
496   </para>
497   <synopsis>
498     class exception {
499     public:
500       exception (int code);
501       int errcode () const;
502       const char *errmsg () const;
503     };
504   </synopsis>
505   <para>
506    It has three concrete subclasses:
507   </para>
508
509   <sect2 id="ZOOM::systemException">
510    <title><literal>ZOOM::systemException</literal></title>
511    <synopsis>
512     class systemException: public exception {
513     public:
514       systemException ();
515       int errcode () const;
516       const char *errmsg () const;
517     };
518    </synopsis>
519    <para>
520     Represents a ``system error'', typically indicating that a system
521     call failed - often in the low-level networking code that
522     underlies Z39.50.  <literal>errcode()</literal> returns the value
523     that the system variable <literal>errno</literal> had at the time
524     the exception was constructed; and <literal>errmsg()</literal>
525     returns a human-readable error-message corresponidng to that error
526     code.
527    </para>
528   </sect2>
529
530   <sect2 id="ZOOM::bib1Exception">
531    <title><literal>ZOOM::bib1Exception</literal></title>
532    <synopsis>
533     class bib1Exception: public exception {
534     public:
535       bib1Exception (int errcode, const char *addinfo);
536       int errcode () const;
537       const char *errmsg () const;
538       const char *addinfo () const;
539     };
540    </synopsis>
541    <para>
542     Represents an error condition communicated by a Z39.50 server.
543     <literal>errcode()</literal> returns the BIB-1 diagnostic code of
544     the error, and <literal>errmsg()</literal> a human-readable error
545     message corresponding to that code.  <literal>addinfo()</literal>
546     returns any additional information associated with the error.
547    </para>
548    <para>
549     For example, if a ZOOM application tries to search in the
550     ``Voyager'' database of a server that does not have a database of
551     that name, a <literal>bib1Exception</literal> will be thrown in
552     which <literal>errcode()</literal> returns 109,
553     <literal>errmsg()</literal> returns the corresponding error
554     message ``Database unavailable'' and <literal>addinfo()</literal>
555     returns the name of the requested, but unavailable, database.
556    </para>
557   </sect2>
558
559   <sect2 id="ZOOM::queryException">
560    <title><literal>ZOOM::queryException</literal></title>
561    <synopsis>
562     class queryException: public exception {
563     public:
564       static const int PREFIX = 1;
565       static const int CCL = 2;
566       queryException (int qtype, const char *source);
567       int errcode () const;
568       const char *errmsg () const;
569       const char *addinfo () const;
570     };
571    </synopsis>
572    <para>
573     This class represents an error in parsing a query into a form that
574     a Z39.50 can understand.  It must be created with the
575     <literal>qtype</literal> parameter equal to one of the query-type
576     constants, which can be retrieved via the
577     <literal>errcode()</literal> method; <literal>errmsg()</literal>
578     returns an error-message specifying which kind of query was
579     malformed; and <literal>addinfo()</literal> returns a copy of the
580     query itself (that is, the value of <literal>source</literal> with
581     which the exception object was created.)
582    </para>
583   </sect2>
584
585   <sect2 id="revised-sample">
586    <title>Revised Sample Program</title>
587    <para>
588     Now we can revise the sample program from the
589     <link linkend="zoom-introduction">introduction</link>
590     to catch exceptions and report any errors:
591    </para>
592    <programlisting>
593     /* g++ -o zoom-c++-hw zoom-c++-hw.cpp -lzoompp -lyaz */
594
595     #include &lt;iostream&gt;
596     #include &lt;yazpp/zoom.h&gt;
597
598     using namespace ZOOM;
599
600     int main(int argc, char **argv)
601     {
602         try {
603             connection conn("z3950.loc.gov", 7090);
604             conn.option("databaseName", "Voyager");
605             conn.option("preferredRecordSyntax", "USMARC");
606             resultSet rs(conn, prefixQuery("@attr 1=7 0253333490"));
607             const record *rec = rs.getRecord(0);
608             cout &lt;&lt; rec-&gt;render() &lt;&lt; endl;
609         } catch (systemException &amp;e) {
610             cerr &lt;&lt; "System error " &lt;&lt;
611                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
612         } catch (bib1Exception &amp;e) {
613             cerr &lt;&lt; "BIB-1 error " &lt;&lt; 
614                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
615         } catch (queryException &amp;e) {
616             cerr &lt;&lt; "Query error " &lt;&lt;
617                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; "): " &lt;&lt; e.addinfo() &lt;&lt; endl;
618         } catch (exception &amp;e) {
619             cerr &lt;&lt; "Error " &lt;&lt;
620                 e.errcode() &lt;&lt; " (" &lt;&lt; e.errmsg() &lt;&lt; ")" &lt;&lt; endl;
621         }
622     }
623    </programlisting>
624    <para>
625     The heart of this program is the same as in the original version,
626     but it's now wrapped in a <literal>try</literal> block followed by
627     several <literal>catch</literal> blocks which try to give helpful
628     diagnostics if something goes wrong.
629    </para>
630    <para>
631     The first such block diagnoses system-level errors such as memory
632     exhaustion or a network connection being broken by a server's
633     untimely death; the second catches errors at the Z39.50 level,
634     such as a server's report that it can't provide records in USMARC
635     syntax; the third is there in case there's something wrong with
636     the syntax of the query (although in this case it's correct); and
637     finally, the last <literal>catch</literal> block is a
638     belt-and-braces measure to be sure that nothing escapes us.
639    </para>
640   </sect2>
641
642   <sect2 id="exception.references">
643    <title>References</title>
644    <itemizedlist>
645     <listitem>
646      <para>
647       <ulink url="http://zoom.z3950.org/api/zoom-1.3.html#3.7"
648         >Section 3.7 (Exception) of the ZOOM Abstract API</ulink>
649      </para>
650     </listitem>
651     <listitem>
652      <para>
653       <ulink url="&url.z39.50.diagnostics;">Bib-1 Diagnostics</ulink> on the
654       <ulink url="&url.z39.50;">Z39.50 Maintenance Agency</ulink> site.
655      </para>
656     </listitem>
657    </itemizedlist>
658    <para>
659     Because C does not support exceptions, ZOOM-C has no API element
660     that corresponds directly with ZOOM-C++'s
661     <literal>exception</literal> class and its subclasses.  The
662     closest thing is the <literal>ZOOM_connection_error</literal>
663     function described in
664      <ulink url="&url.yaz.zoom.connections;"
665         >The Connections section</ulink> of the documentation.
666    </para>
667   </sect2>
668  </sect1>
669
670 </chapter>
671
672  <!-- Keep this comment at the end of the file
673  Local variables:
674  mode: sgml
675  sgml-omittag:t
676  sgml-shorttag:t
677  sgml-minimize-attributes:nil
678  sgml-always-quote-attributes:t
679  sgml-indent-step:1
680  sgml-indent-data:t
681  sgml-parent-document: "yazpp.xml"
682  sgml-local-catalogs: nil
683  sgml-namecase-general:t
684  End:
685  -->