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