Fix switch label
[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 const char *lookup_relation_index_from_attr(Z_AttributeList *attributes)
50 {
51     int j;
52     for (j = 0; j < attributes->num_attributes; j++)
53     {
54         Z_AttributeElement *ae = attributes->attributes[j];
55         if (*ae->attributeType == 2) /* relation attribute */
56         {
57             if (ae->which == Z_AttributeValue_numeric)
58             {
59                 /* Only support for numeric relation */
60                 Odr_int *relation = ae->value.numeric;
61                 /* map this numeric to representation in CQL */
62                 switch (*relation) {
63                     /* Unsure on whether this is the relation attribute constants? */
64                     case Z_ProximityOperator_Prox_lessThan: 
65                         return "<";
66                     case Z_ProximityOperator_Prox_lessThanOrEqual: 
67                         return "<="; 
68                     case Z_ProximityOperator_Prox_equal: 
69                         return "="; 
70                     case Z_ProximityOperator_Prox_greaterThanOrEqual: 
71                         return ">="; 
72                     case Z_ProximityOperator_Prox_greaterThan: 
73                         return ">"; 
74                     case Z_ProximityOperator_Prox_notEqual: 
75                         return "<>"; 
76                     case 100: 
77                         /* phonetic is not supported in CQL */
78                         return 0; 
79                     case 101: 
80                         /* stem is not supported in CQL */
81                         return 0; 
82                     case 102: 
83                         /* relevance is supported in CQL, but not implemented yet */
84                         return 0; 
85                 default:
86                         /* Invalid relation */
87                         return 0;
88                 }
89             }
90             else {
91                 /*  Can we have a complex relation value?
92                     Should we implement something?
93                  */
94             }
95         }
96     }
97     return "=";
98 }
99
100 static int rpn2cql_attr(cql_transform_t ct,
101                         Z_AttributeList *attributes, WRBUF w)
102 {
103     const char *relation = cql_lookup_reverse(ct, "relation.", attributes);
104     const char *index = cql_lookup_reverse(ct, "index.", attributes);
105     const char *structure = cql_lookup_reverse(ct, "structure.", attributes);
106
107     /* if transform (properties) do not match, we'll just use a USE string attribute (bug #2978) */
108     if (!index)
109         index = lookup_index_from_string_attr(attributes);
110
111     /* Attempt to fix bug #2978: Look for a relation attribute */
112     if (!relation) 
113         relation = lookup_relation_index_from_attr(attributes);
114
115     if (!index)
116     {
117         cql_transform_set_error(ct,
118                                 YAZ_BIB1_UNSUPP_USE_ATTRIBUTE, 0);
119         return -1;
120     }
121     /* for serverChoice we omit index+relation+structure */
122     if (strcmp(index, "cql.serverChoice"))
123     {
124         wrbuf_puts(w, index);
125         if (relation)
126         {
127             if (!strcmp(relation, "exact"))
128                 relation = "==";
129             else if (!strcmp(relation, "eq"))
130                 relation = "=";
131             else if (!strcmp(relation, "le"))
132                 relation = "<=";
133             else if (!strcmp(relation, "ge"))
134                 relation = ">=";
135             /* Missing mapping of not equal, phonetic, stem and relevance */
136             wrbuf_puts(w, relation);
137         }
138         else
139             wrbuf_puts(w, "=");
140
141         if (structure)
142         {
143             if (strcmp(structure, "*"))
144             {
145                 wrbuf_puts(w, "/");
146                 wrbuf_puts(w, structure);
147                 wrbuf_puts(w, " ");
148             }
149         }
150     }
151     return 0;
152 }
153
154 /* Bug 2878: Currently only support left and right truncation. Specific check for this */
155 static int checkForTruncation(int flag, Z_AttributeList *attributes) {
156     int j;
157     int server_choice = 1;
158     for (j = 0; j < attributes->num_attributes; j++)
159     {
160         Z_AttributeElement *ae = attributes->attributes[j];
161         if (*ae->attributeType == 5) /* truncation attribute */
162         {
163             if (ae->which == Z_AttributeValue_numeric)
164             {
165                                 int truncation = *(ae->value.numeric);
166                         /* This logic only works for Left, right and both. eg. 1,2,3 */
167                 if (truncation <= 3)
168                         return (int) (truncation & flag);
169             }
170             /* Complex: Shouldn't happen */
171         }
172     }
173     /* No truncation or unsupported */
174     return 0;
175 };
176
177 static int checkForLeftTruncation(Z_AttributeList *attributes) {
178         return checkForTruncation(1, attributes);
179 }
180
181 static int checkForRightTruncation(Z_AttributeList *attributes) {
182         return checkForTruncation(2, attributes);
183 };
184
185 static int rpn2cql_simple(cql_transform_t ct,
186                           void (*pr)(const char *buf, void *client_data),
187                           void *client_data,
188                           Z_Operand *q, WRBUF w)
189 {
190     int ret = 0;
191     if (q->which != Z_Operand_APT)
192     {
193         ret = -1;
194         cql_transform_set_error(ct, YAZ_BIB1_RESULT_SET_UNSUPP_AS_A_SEARCH_TERM, 0);
195     }
196     else
197     {
198         Z_AttributesPlusTerm *apt = q->u.attributesPlusTerm;
199         Z_Term *term = apt->term;
200         const char *sterm = 0;
201         size_t lterm = 0;
202
203         wrbuf_rewind(w);
204         ret = rpn2cql_attr(ct, apt->attributes, w);
205
206         switch(term->which)
207         {
208         case Z_Term_general:
209             lterm = term->u.general->len;
210             sterm = (const char *) term->u.general->buf;
211             break;
212         case Z_Term_numeric:
213             wrbuf_printf(w, ODR_INT_PRINTF, *term->u.numeric);
214             break;
215         case Z_Term_characterString:
216             sterm = term->u.characterString;
217             lterm = strlen(sterm);
218             break;
219         default:
220             ret = -1;
221             cql_transform_set_error(ct, YAZ_BIB1_TERM_TYPE_UNSUPP, 0);
222         }
223
224         if (term)
225         {
226             size_t i;
227             int must_quote = 0;
228             for (i = 0 ; i < lterm; i++)
229                 if (sterm[i] == ' ')
230                     must_quote = 1;
231             if (must_quote)
232                 wrbuf_puts(w, "\"");
233             /* Bug 2878: Check and add Truncation */
234                         if (checkForLeftTruncation(apt->attributes))
235                 wrbuf_puts(w, "*");
236             wrbuf_write(w, sterm, lterm);
237             /* Bug 2878: Check and add Truncation */
238                         if (checkForRightTruncation(apt->attributes))
239                 wrbuf_puts(w, "*");
240             if (must_quote)
241                 wrbuf_puts(w, "\"");
242         }
243         if (ret == 0)
244             pr(wrbuf_cstr(w), client_data);
245     }
246     return ret;
247 }
248
249
250 static int rpn2cql_structure(cql_transform_t ct,
251                              void (*pr)(const char *buf, void *client_data),
252                              void *client_data,
253                              Z_RPNStructure *q, int nested,
254                              WRBUF w)
255 {
256     if (q->which == Z_RPNStructure_simple)
257         return rpn2cql_simple(ct, pr, client_data, q->u.simple, w);
258     else
259     {
260         Z_Operator *op = q->u.complex->roperator;
261         int r;
262
263         if (nested)
264             pr("(", client_data);
265
266         r = rpn2cql_structure(ct, pr, client_data, q->u.complex->s1, 1, w);
267         if (r)
268             return r;
269         switch(op->which)
270         {
271         case  Z_Operator_and:
272             pr(" and ", client_data);
273             break;
274         case  Z_Operator_or:
275             pr(" or ", client_data);
276             break;
277         case  Z_Operator_and_not:
278             pr(" not ", client_data);
279             break;
280         case  Z_Operator_prox:
281             cql_transform_set_error(ct, YAZ_BIB1_UNSUPP_SEARCH, 0);
282             return -1;
283         }
284         r = rpn2cql_structure(ct, pr, client_data, q->u.complex->s2, 1, w);
285         if (nested)
286             pr(")", client_data);
287         return r;
288     }
289 }
290
291 int cql_transform_rpn2cql_stream(cql_transform_t ct,
292                                  void (*pr)(const char *buf, void *client_data),
293                                  void *client_data,
294                                  Z_RPNQuery *q)
295 {
296     int r;
297     WRBUF w = wrbuf_alloc();
298     cql_transform_set_error(ct, 0, 0);
299     r = rpn2cql_structure(ct, pr, client_data, q->RPNStructure, 0, w);
300     wrbuf_destroy(w);
301     return r;
302 }
303
304
305 int cql_transform_rpn2cql_wrbuf(cql_transform_t ct,
306                                 WRBUF w,
307                                 Z_RPNQuery *q)
308 {
309     return cql_transform_rpn2cql_stream(ct, wrbuf_vputs, w, q);
310 }
311
312 /*
313  * Local variables:
314  * c-basic-offset: 4
315  * c-file-style: "Stroustrup"
316  * indent-tabs-mode: nil
317  * End:
318  * vim: shiftwidth=4 tabstop=8 expandtab
319  */
320