Version 3.0.49. Update news.
[yaz-moved-to-github.git] / src / rpn2cql.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2009 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file
8  * \brief Implements RPN to CQL conversion
9  *
10  */
11
12 #include <assert.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <yaz/rpn2cql.h>
16 #include <yaz/xmalloc.h>
17 #include <yaz/diagbib1.h>
18 #include <yaz/z-core.h>
19 #include <yaz/wrbuf.h>
20
21 static const char *lookup_index_from_string_attr(Z_AttributeList *attributes)
22 {
23     int j;
24     int server_choice = 1;
25     for (j = 0; j < attributes->num_attributes; j++)
26     {
27         Z_AttributeElement *ae = attributes->attributes[j];
28         if (*ae->attributeType == 1) /* use attribute */
29         {
30             if (ae->which == Z_AttributeValue_complex)
31             {
32                 Z_ComplexAttribute *ca = ae->value.complex;
33                 int i;
34                 for (i = 0; i < ca->num_list; i++)
35                 {
36                     Z_StringOrNumeric *son = ca->list[i];
37                     if (son->which == Z_StringOrNumeric_string)
38                         return son->u.string;
39                 }
40             }
41             server_choice = 0; /* not serverChoice because we have use attr */
42         }
43     }
44     if (server_choice)
45         return "cql.serverChoice";
46     return 0;
47 }
48
49 static int rpn2cql_attr(cql_transform_t ct,
50                         Z_AttributeList *attributes, WRBUF w)
51 {
52     const char *relation = cql_lookup_reverse(ct, "relation.", attributes);
53     const char *index = cql_lookup_reverse(ct, "index.", attributes);
54     const char *structure = cql_lookup_reverse(ct, "structure.", attributes);
55
56     /* if transform (properties) do not match, we'll just use a USE
57        string attribute (bug #2978) */
58     if (!index)
59         index = lookup_index_from_string_attr(attributes);
60
61     if (!index)
62     {
63         cql_transform_set_error(ct,
64                                 YAZ_BIB1_UNSUPP_USE_ATTRIBUTE, 0);
65         return -1;
66     }
67     /* for serverChoice we omit index+relation+structure */
68     if (strcmp(index, "cql.serverChoice"))
69     {
70         wrbuf_puts(w, index);
71         if (relation)
72         {
73             if (!strcmp(relation, "exact"))
74                 relation = "==";
75             else if (!strcmp(relation, "eq"))
76                 relation = "=";
77             else if (!strcmp(relation, "le"))
78                 relation = "<=";
79             else if (!strcmp(relation, "ge"))
80                 relation = ">=";
81             wrbuf_puts(w, relation);
82         }
83         else
84             wrbuf_puts(w, "=");
85
86         if (structure)
87         {
88             if (strcmp(structure, "*"))
89             {
90                 wrbuf_puts(w, "/");
91                 wrbuf_puts(w, structure);
92                 wrbuf_puts(w, " ");
93             }
94         }
95     }
96     return 0;
97 }
98
99 static int rpn2cql_simple(cql_transform_t ct,
100                           void (*pr)(const char *buf, void *client_data),
101                           void *client_data,
102                           Z_Operand *q, WRBUF w)
103 {
104     int ret = 0;
105     if (q->which != Z_Operand_APT)
106     {
107         ret = -1;
108         cql_transform_set_error(ct, YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM, 0);
109     }
110     else
111     {
112         Z_AttributesPlusTerm *apt = q->u.attributesPlusTerm;
113         Z_Term *term = apt->term;
114         const char *sterm = 0;
115         size_t lterm = 0;
116
117         wrbuf_rewind(w);
118         ret = rpn2cql_attr(ct, apt->attributes, w);
119
120         switch(term->which)
121         {
122         case Z_Term_general:
123             lterm = term->u.general->len;
124             sterm = (const char *) term->u.general->buf;
125             break;
126         case Z_Term_numeric:
127             wrbuf_printf(w, ODR_INT_PRINTF, *term->u.numeric);
128             break;
129         case Z_Term_characterString:
130             sterm = term->u.characterString;
131             lterm = strlen(sterm);
132             break;
133         default:
134             ret = -1;
135             cql_transform_set_error(ct, YAZ_BIB1_TERM_TYPE_UNSUPP, 0);
136         }
137
138         if (term)
139         {
140             size_t i;
141             int must_quote = 0;
142             for (i = 0 ; i < lterm; i++)
143                 if (sterm[i] == ' ')
144                     must_quote = 1;
145             if (must_quote)
146                 wrbuf_puts(w, "\"");
147             wrbuf_write(w, sterm, lterm);
148             if (must_quote)
149                 wrbuf_puts(w, "\"");
150         }
151         if (ret == 0)
152             pr(wrbuf_cstr(w), client_data);
153     }
154     return ret;
155 }
156
157 static int rpn2cql_structure(cql_transform_t ct,
158                              void (*pr)(const char *buf, void *client_data),
159                              void *client_data,
160                              Z_RPNStructure *q, int nested,
161                              WRBUF w)
162 {
163     if (q->which == Z_RPNStructure_simple)
164         return rpn2cql_simple(ct, pr, client_data, q->u.simple, w);
165     else
166     {
167         Z_Operator *op = q->u.complex->roperator;
168         int r;
169
170         if (nested)
171             pr("(", client_data);
172
173         r = rpn2cql_structure(ct, pr, client_data, q->u.complex->s1, 1, w);
174         if (r)
175             return r;
176         switch(op->which)
177         {
178         case  Z_Operator_and:
179             pr(" and ", client_data);
180             break;
181         case  Z_Operator_or:
182             pr(" or ", client_data);
183             break;
184         case  Z_Operator_and_not:
185             pr(" not ", client_data);
186             break;
187         case  Z_Operator_prox:
188             cql_transform_set_error(ct, YAZ_BIB1_UNSUPP_SEARCH, 0);
189             return -1;
190         }
191         r = rpn2cql_structure(ct, pr, client_data, q->u.complex->s2, 1, w);
192         if (nested)
193             pr(")", client_data);
194         return r;
195     }
196 }
197
198 int cql_transform_rpn2cql_stream(cql_transform_t ct,
199                                  void (*pr)(const char *buf, void *client_data),
200                                  void *client_data,
201                                  Z_RPNQuery *q)
202 {
203     int r;
204     WRBUF w = wrbuf_alloc();
205     cql_transform_set_error(ct, 0, 0);
206     r = rpn2cql_structure(ct, pr, client_data, q->RPNStructure, 0, w);
207     wrbuf_destroy(w);
208     return r;
209 }
210
211
212 int cql_transform_rpn2cql_wrbuf(cql_transform_t ct,
213                                 WRBUF w,
214                                 Z_RPNQuery *q)
215 {
216     return cql_transform_rpn2cql_stream(ct, wrbuf_vputs, w, q);
217 }
218
219 /*
220  * Local variables:
221  * c-basic-offset: 4
222  * c-file-style: "Stroustrup"
223  * indent-tabs-mode: nil
224  * End:
225  * vim: shiftwidth=4 tabstop=8 expandtab
226  */
227