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