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