Re-implemented the element name encoding as Adams suggestion: <e tag="value"> when...
[yaz-moved-to-github.git] / src / xmlquery.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /** \file xmlquery.c
7     \brief Query / XML conversions
8 */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <assert.h>
13
14 #if YAZ_HAVE_XML2
15 #include <libxml/parser.h>
16 #include <libxml/tree.h>
17
18 #include <yaz/logrpn.h>
19 #include <yaz/xmlquery.h>
20 #include <yaz/nmem_xml.h>
21 #include <yaz/oid_db.h>
22
23 static int check_diagnostic(const xmlNode *ptr, ODR odr,
24                             int *error_code, const char **addinfo)
25 {
26     if (ptr && ptr->type == XML_ELEMENT_NODE &&
27         !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
28     {
29         struct _xmlAttr *attr;
30         const char *code_str = 0;
31         const char *addinfo_str = 0;
32         for (attr = ptr->properties; attr; attr = attr->next)
33         {
34             if (!xmlStrcmp(attr->name, BAD_CAST "code") &&
35                 attr->children && attr->children->type == XML_TEXT_NODE)
36                 code_str = (const char *) attr->children->content;
37             else if (!xmlStrcmp(attr->name, BAD_CAST "addinfo") &&
38                      attr->children && attr->children->type == XML_TEXT_NODE)
39                 addinfo_str = (const char *) attr->children->content;
40             else
41             {
42                 *error_code = 1;
43                 *addinfo = "bad attribute for diagnostic element";
44                 return 1;
45             }
46         }
47         if (!code_str)
48         {
49             *error_code = 1;
50             *addinfo = "missing @code for diagnostic element";
51             return 1;
52         }
53         *error_code = atoi(code_str);
54         if (addinfo_str)
55             *addinfo = odr_strdup(odr, addinfo_str);
56         return 1;
57     }
58     else
59         return 0;
60 }
61
62 static void yaz_query2xml_attribute_element(const Z_AttributeElement *element,
63                                             xmlNodePtr parent)
64 {
65     char formstr[30];
66     const char *setname = 0;
67     char oid_name_str[OID_STR_MAX];
68     
69     if (element->attributeSet)
70     {
71         setname = yaz_oid_to_string_buf(element->attributeSet,
72                                         0, oid_name_str);
73     }
74
75     if (element->which == Z_AttributeValue_numeric)
76     {
77         xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
78
79         if (setname)
80             xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
81
82         assert(*element->attributeType > 0 && *element->attributeType < 20);
83         sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
84         xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
85
86         sprintf(formstr, ODR_INT_PRINTF, *element->value.numeric);
87         xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
88     }
89     else if (element->which == Z_AttributeValue_complex)
90     {
91         int i;
92         for (i = 0; i<element->value.complex->num_list; i++)
93         {
94             xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
95             
96             if (setname)
97                 xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
98             
99             sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
100             xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
101             
102             if (element->value.complex->list[i]->which ==
103                 Z_StringOrNumeric_string)
104             {
105                 xmlNewProp(node, BAD_CAST "value", BAD_CAST 
106                            element->value.complex->list[i]->u.string);
107             }
108             else if (element->value.complex->list[i]->which ==
109                      Z_StringOrNumeric_numeric)
110             {
111                 sprintf(formstr, ODR_INT_PRINTF,
112                         *element->value.complex->list[i]->u.numeric);
113                 xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
114             }
115         }
116     }
117 }
118
119
120 static xmlNodePtr yaz_query2xml_term(const Z_Term *term, xmlNodePtr parent)
121 {
122     xmlNodePtr t = 0;
123     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "term", 0);
124     char formstr[20];
125     const char *type = 0;
126
127     switch (term->which)
128     {
129     case Z_Term_general:
130         type = "general";
131         t = xmlNewTextLen(BAD_CAST term->u.general->buf, term->u.general->len);
132         break;
133     case Z_Term_numeric:
134         type = "numeric";
135         sprintf(formstr, ODR_INT_PRINTF, *term->u.numeric);
136         t = xmlNewText(BAD_CAST formstr);       
137         break;
138     case Z_Term_characterString:
139         type = "string";
140         t = xmlNewText(BAD_CAST term->u.characterString);
141         break;
142     case Z_Term_oid:
143         type = "oid";
144         break;
145     case Z_Term_dateTime:
146         type = "dateTime";
147         break;
148     case Z_Term_external:
149         type = "external";
150         break;
151     case Z_Term_integerAndUnit:
152         type ="integerAndUnit";
153         break;
154     case Z_Term_null:
155         type = "null";
156         break;
157     default:
158         break;
159     }
160     if (t) /* got a term node ? */
161         xmlAddChild(node, t);
162     if (type)
163         xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
164     return node;
165 }
166
167 static xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
168                                     xmlNodePtr parent)
169 {
170     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "apt", 0);
171     int num_attributes = zapt->attributes->num_attributes;
172     int i;
173     for (i = 0; i<num_attributes; i++)
174         yaz_query2xml_attribute_element(zapt->attributes->attributes[i], node);
175     yaz_query2xml_term(zapt->term, node);
176
177     return node;
178 }
179
180
181 static void yaz_query2xml_operator(Z_Operator *op, xmlNodePtr node)
182 {
183     const char *type = 0;
184     switch(op->which)
185     {
186     case Z_Operator_and:
187         type = "and";
188         break;
189     case Z_Operator_or:
190         type = "or";
191         break;
192     case Z_Operator_and_not:
193         type = "not";
194         break;
195     case Z_Operator_prox:
196         type = "prox";
197         break;
198     default:
199         return;
200     }
201     xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
202     
203     if (op->which == Z_Operator_prox)
204     {
205         char formstr[30];
206         
207         if (op->u.prox->exclusion)
208         {
209             if (*op->u.prox->exclusion)
210                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "true");
211             else
212                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "false");
213         }
214         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->distance);
215         xmlNewProp(node, BAD_CAST "distance", BAD_CAST formstr);
216
217         if (*op->u.prox->ordered)
218             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "true");
219         else 
220             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "false");
221        
222         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->relationType);
223         xmlNewProp(node, BAD_CAST "relationType", BAD_CAST formstr);
224         
225         switch(op->u.prox->which)
226         {
227         case Z_ProximityOperator_known:
228             sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->u.known);
229             xmlNewProp(node, BAD_CAST "knownProximityUnit",
230                        BAD_CAST formstr);
231             break;
232         case Z_ProximityOperator_private:
233         default:
234             xmlNewProp(node, BAD_CAST "privateProximityUnit",
235                        BAD_CAST "private");
236             break;
237         }
238     }
239 }
240
241 static xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
242                                              xmlNodePtr parent)
243 {
244     if (zs->which == Z_RPNStructure_complex)
245     {
246         Z_Complex *zc = zs->u.complex;
247
248         xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "operator", 0);
249         if (zc->roperator)
250             yaz_query2xml_operator(zc->roperator, node);
251         yaz_query2xml_rpnstructure(zc->s1, node);
252         yaz_query2xml_rpnstructure(zc->s2, node);
253         return node;
254     }
255     else if (zs->which == Z_RPNStructure_simple)
256     {
257         if (zs->u.simple->which == Z_Operand_APT)
258             return yaz_query2xml_apt(zs->u.simple->u.attributesPlusTerm,
259                                      parent);
260         else if (zs->u.simple->which == Z_Operand_resultSetId)
261             return xmlNewChild(parent, /* NS */ 0, BAD_CAST "rset", 
262                                BAD_CAST zs->u.simple->u.resultSetId);
263     }
264     return 0;
265 }
266
267 static xmlNodePtr yaz_query2xml_rpn(const Z_RPNQuery *rpn, xmlNodePtr parent)
268 {
269     if (rpn->attributeSetId)
270     {
271         char oid_name_str[OID_STR_MAX];
272         const char *setname = yaz_oid_to_string_buf(rpn->attributeSetId,
273                                                     0, oid_name_str);
274         if (setname)
275             xmlNewProp(parent, BAD_CAST "set", BAD_CAST setname);
276     }
277     return yaz_query2xml_rpnstructure(rpn->RPNStructure, parent);
278 }
279
280 static xmlNodePtr yaz_query2xml_ccl(const Odr_oct *ccl, xmlNodePtr node)
281 {
282     return 0;
283 }
284
285 static xmlNodePtr yaz_query2xml_z3958(const Odr_oct *ccl, xmlNodePtr node)
286 {
287     return 0;
288 }
289
290 static xmlNodePtr yaz_query2xml_cql(const char *cql, xmlNodePtr node)
291 {
292     return 0;
293 }
294
295 void yaz_rpnquery2xml(const Z_RPNQuery *rpn, xmlDocPtr *docp)
296 {
297     Z_Query query;
298
299     query.which = Z_Query_type_1;
300     query.u.type_1 = (Z_RPNQuery *) rpn;
301     yaz_query2xml(&query, docp);
302 }
303
304 void yaz_query2xml(const Z_Query *q, xmlDocPtr *docp)
305 {
306     xmlNodePtr top_node, q_node = 0, child_node = 0;
307
308     assert(q);
309     assert(docp);
310
311     top_node = xmlNewNode(0, BAD_CAST "query");
312
313     switch (q->which)
314     {
315     case Z_Query_type_1: 
316     case Z_Query_type_101:
317         q_node = xmlNewChild(top_node, 0, BAD_CAST "rpn", 0);
318         child_node = yaz_query2xml_rpn(q->u.type_1, q_node);
319         break;
320     case Z_Query_type_2:
321         q_node = xmlNewChild(top_node, 0, BAD_CAST "ccl", 0);
322         child_node = yaz_query2xml_ccl(q->u.type_2, q_node);
323         break;
324     case Z_Query_type_100:
325         q_node = xmlNewChild(top_node, 0, BAD_CAST "z39.58", 0);
326         child_node = yaz_query2xml_z3958(q->u.type_100, q_node);
327         break;
328     case Z_Query_type_104:
329         if (q->u.type_104->which == Z_External_CQL)
330         {
331             q_node = xmlNewChild(top_node, 0, BAD_CAST "cql", 0);
332             child_node = yaz_query2xml_cql(q->u.type_104->u.cql, q_node);
333         }
334     }
335     if (child_node && q_node)
336     {
337         *docp = xmlNewDoc(BAD_CAST "1.0");
338         xmlDocSetRootElement(*docp, top_node); /* make it top node in doc */
339     }
340     else
341     {
342         *docp = 0;
343         xmlFreeNode(top_node);
344     }
345 }
346
347 static bool_t *boolVal(ODR odr, const char *str)
348 {
349     if (*str == '\0' || strchr("0fF", *str))
350         return odr_booldup(odr, 0);
351     return odr_booldup(odr, 1);
352 }
353
354 static Odr_int *intVal(ODR odr, const char *str)
355 {
356     return odr_intdup(odr, atoi(str));
357 }
358
359 static void yaz_xml2query_operator(const xmlNode *ptr, Z_Operator **op,
360                                    ODR odr,
361                                    int *error_code, const char **addinfo)
362 {
363     xmlChar *type = xmlGetProp((xmlNodePtr) ptr, BAD_CAST "type");
364     if (!type)
365     {
366         *error_code = 1;
367         *addinfo = "no operator type";
368         return;
369     }
370     *op = (Z_Operator*) odr_malloc(odr, sizeof(Z_Operator));
371     if (!xmlStrcmp(type, BAD_CAST "and"))
372     {
373         (*op)->which = Z_Operator_and;
374         (*op)->u.op_and = odr_nullval();
375     }
376     else if (!xmlStrcmp(type, BAD_CAST "or"))
377     {
378         (*op)->which = Z_Operator_or;
379         (*op)->u.op_or = odr_nullval();
380     }
381     else if (!xmlStrcmp(type, BAD_CAST "not"))
382     {
383         (*op)->which = Z_Operator_and_not;
384         (*op)->u.and_not = odr_nullval();
385     }
386     else if (!xmlStrcmp(type, BAD_CAST "prox"))
387     {
388         struct _xmlAttr *attr;
389         Z_ProximityOperator *pop = (Z_ProximityOperator *)
390             odr_malloc(odr, sizeof(*pop));
391         (*op)->which = Z_Operator_prox;
392         (*op)->u.prox = pop;
393         /* default values */
394         pop->exclusion = 0;
395         pop->ordered = odr_booldup(odr, 1);
396         pop->relationType =
397             odr_intdup(odr, Z_ProximityOperator_Prox_lessThanOrEqual);
398         pop->which = Z_ProximityOperator_known;
399         pop->u.known = odr_intdup(odr, Z_ProxUnit_word);
400         pop->distance = odr_intdup(odr, 1);
401
402         for (attr = ptr->properties; attr; attr = attr->next)
403         {
404             const char *value = (const char *) attr->children->content;
405             if (!xmlStrcmp(attr->name, BAD_CAST "type"))
406                 ;
407             else if (!xmlStrcmp(attr->name, BAD_CAST "exclusion"))
408                 pop->exclusion = boolVal(odr, value);
409             else if (!xmlStrcmp(attr->name, BAD_CAST "distance"))
410                 pop->distance = intVal(odr, value);
411             else if (!xmlStrcmp(attr->name, BAD_CAST "ordered"))
412                 pop->ordered = boolVal(odr, value);
413             else if (!xmlStrcmp(attr->name, BAD_CAST "relationType"))
414                 pop->relationType = intVal(odr, value);
415             else if (!xmlStrcmp(attr->name, BAD_CAST "knownProximityUnit"))
416             {
417                 pop->which = Z_ProximityOperator_known;
418                 pop->u.known = intVal(odr, value);
419             }
420             else if (!xmlStrcmp(attr->name, BAD_CAST "privateProximityUnit"))
421             {
422                 pop->which = Z_ProximityOperator_private;
423                 pop->u.known = intVal(odr, value);
424             }
425             else
426             {
427                 *error_code = 1;
428                 *addinfo = "bad proximity attribute";
429                 break;
430             }
431         }
432     }
433     else
434     {
435         *error_code = 1;
436         *addinfo = "bad operator type";
437     }
438     xmlFree(type);
439 }
440
441 static void yaz_xml2query_attribute_element(const xmlNode *ptr, 
442                                             Z_AttributeElement **elem, ODR odr,
443                                             int *error_code,
444                                             const char **addinfo)
445 {
446     int i;
447     xmlChar *set = 0;
448     xmlChar *type = 0;
449     xmlChar *value = 0;
450     int num_values = 0;
451     struct _xmlAttr *attr;
452     for (attr = ptr->properties; attr; attr = attr->next)
453     {
454         if (!xmlStrcmp(attr->name, BAD_CAST "set") &&
455             attr->children && attr->children->type == XML_TEXT_NODE)
456             set = attr->children->content;
457         else if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
458             attr->children && attr->children->type == XML_TEXT_NODE)
459             type = attr->children->content;
460         else if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
461             attr->children && attr->children->type == XML_TEXT_NODE)
462         {
463             value = attr->children->content;
464             num_values++;
465         }
466         else
467         {
468             *error_code = 1;
469             *addinfo = "bad attribute for attr content";
470             return;
471         }
472     }
473     if (!type)
474     {
475         *error_code = 1;
476         *addinfo = "missing type attribute for att content";
477         return;
478     }
479     if (!value)
480     {
481         *error_code = 1;
482         *addinfo = "missing value attribute for att content";
483         return;
484     }
485         
486     *elem = (Z_AttributeElement *) odr_malloc(odr, sizeof(**elem));
487     if (set)
488         (*elem)->attributeSet = yaz_string_to_oid_odr(yaz_oid_std(),
489                                                       CLASS_ATTSET,
490                                                       (const char *) set,
491                                                       odr);
492     else
493         (*elem)->attributeSet = 0;
494     (*elem)->attributeType = intVal(odr, (const char *) type);
495
496     /* looks like a number ? */
497     for (i = 0; value[i] && value[i] >= '0' && value[i] <= '9'; i++)
498         ;
499     if (num_values > 1 || value[i])
500     {   /* multiple values or string, so turn to complex attribute */
501         (*elem)->which = Z_AttributeValue_complex;
502         (*elem)->value.complex =
503             (Z_ComplexAttribute*) odr_malloc(odr, sizeof(Z_ComplexAttribute));
504         (*elem)->value.complex->num_list = num_values;
505         (*elem)->value.complex->list = (Z_StringOrNumeric **)
506             odr_malloc(odr, sizeof(Z_StringOrNumeric*) * num_values);
507
508         /* second pass over attr values */
509         i = 0;
510         for (attr = ptr->properties; attr; attr = attr->next)
511         {
512             if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
513                 attr->children && attr->children->type == XML_TEXT_NODE)
514             {
515                 const char *val = (const char *) attr->children->content;
516                 assert (i < num_values);
517                 (*elem)->value.complex->list[i] = (Z_StringOrNumeric *)
518                     odr_malloc(odr, sizeof(Z_StringOrNumeric));
519                 (*elem)->value.complex->list[i]->which =
520                     Z_StringOrNumeric_string;
521                 (*elem)->value.complex->list[i]->u.string =
522                     odr_strdup(odr, val);
523                 i++;
524             }
525         }
526         (*elem)->value.complex->num_semanticAction = 0;
527         (*elem)->value.complex->semanticAction = 0;        
528     }
529     else
530     {   /* good'ld numeric value */
531         (*elem)->which = Z_AttributeValue_numeric;
532         (*elem)->value.numeric = intVal(odr, (const char *) value);
533     }
534 }
535
536 static char *strVal(const xmlNode *ptr_cdata, ODR odr)
537 {
538     return nmem_text_node_cdata(ptr_cdata, odr_getmem(odr));
539 }
540
541 static void yaz_xml2query_term(const xmlNode *ptr, Z_Term **term, ODR odr,
542                                int *error_code, const char **addinfo)
543 {
544     xmlChar *type = 0;
545     struct _xmlAttr *attr;
546     char *cdata = strVal(ptr->children, odr);
547
548     for (attr = ptr->properties; attr; attr = attr->next)
549     {
550         if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
551             attr->children && attr->children->type == XML_TEXT_NODE)
552             type = attr->children->content;
553         else
554         {
555             *error_code = 1;
556             *addinfo = "bad attribute for attr content";
557             return;
558         }
559     }
560     *term = (Z_Term *) odr_malloc(odr, sizeof(Z_Term));
561
562     if (!type || !xmlStrcmp(type, BAD_CAST "general"))
563     {
564         (*term)->which = Z_Term_general;
565         (*term)->u.general =
566             odr_create_Odr_oct(odr, (unsigned char *)cdata, strlen(cdata));
567     }
568     else if (!xmlStrcmp(type, BAD_CAST "numeric"))
569     {
570         (*term)->which = Z_Term_numeric;
571         (*term)->u.numeric = intVal(odr, cdata);
572     }
573     else if (!xmlStrcmp(type, BAD_CAST "string"))
574     {
575         (*term)->which = Z_Term_characterString;
576         (*term)->u.characterString = cdata;
577     }
578     else if (!xmlStrcmp(type, BAD_CAST "oid"))
579     {
580         *error_code = 1;
581         *addinfo = "unhandled term type: oid";
582     }
583     else if (!xmlStrcmp(type, BAD_CAST "dateTime"))
584     {
585         *error_code = 1;
586         *addinfo = "unhandled term type: dateTime";
587     }
588     else if (!xmlStrcmp(type, BAD_CAST "integerAndUnit"))
589     {
590         *error_code = 1;
591         *addinfo = "unhandled term type: integerAndUnit";
592     }
593     else if (!xmlStrcmp(type, BAD_CAST "null"))
594     {
595         (*term)->which = Z_Term_null;
596         (*term)->u.null = odr_nullval();
597     }
598     else
599     {
600         *error_code = 1;
601         *addinfo = "unhandled term type";
602     }
603 }
604
605 static void yaz_xml2query_apt(const xmlNode *ptr_apt,
606                               Z_AttributesPlusTerm **zapt, ODR odr,
607                               int *error_code, const char **addinfo)
608 {
609     const xmlNode *ptr = ptr_apt->children;
610     int i, num_attr = 0;
611
612     *zapt = (Z_AttributesPlusTerm *)
613         odr_malloc(odr, sizeof(Z_AttributesPlusTerm));
614
615     /* deal with attributes */
616     (*zapt)->attributes = (Z_AttributeList*)
617         odr_malloc(odr, sizeof(Z_AttributeList));
618
619     /* how many attributes? */
620     for (; ptr; ptr = ptr->next)
621         if (ptr->type == XML_ELEMENT_NODE)
622         {
623             if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
624                 num_attr++;
625             else
626                 break;
627         }
628
629     /* allocate and parse for real */
630     (*zapt)->attributes->num_attributes = num_attr;
631     (*zapt)->attributes->attributes = (Z_AttributeElement **)
632         odr_malloc(odr, sizeof(Z_AttributeElement*) * num_attr);
633
634     i = 0;    
635     ptr = ptr_apt->children;
636     for (; ptr; ptr = ptr->next)
637         if (ptr->type == XML_ELEMENT_NODE)
638         {
639             if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
640             {
641                 yaz_xml2query_attribute_element(
642                     ptr,  &(*zapt)->attributes->attributes[i], odr,
643                     error_code, addinfo);
644                 i++;
645             }
646             else
647                 break;
648         }
649     if (check_diagnostic(ptr, odr, error_code, addinfo))
650         return;
651
652     if (ptr && ptr->type == XML_ELEMENT_NODE)
653     {
654         if (!xmlStrcmp(ptr->name, BAD_CAST "term"))
655         {        
656             /* deal with term */
657             yaz_xml2query_term(ptr, &(*zapt)->term, odr, error_code, addinfo);
658         }
659         else
660         {
661             *error_code = 1;
662             *addinfo = "bad element in apt content";
663         }
664     }
665     else
666     {
667         *error_code = 1;
668         *addinfo = "missing term node in apt content";
669     }
670 }
671
672 static void yaz_xml2query_rset(const xmlNode *ptr, Z_ResultSetId **rset,
673                                ODR odr, int *error_code, const char **addinfo)
674 {
675     if (ptr->children)
676     {
677         *rset = strVal(ptr->children, odr);
678     }
679     else
680     {
681         *error_code = 1;
682         *addinfo = "missing rset content";
683     }
684 }
685
686 static void yaz_xml2query_rpnstructure(const xmlNode *ptr, Z_RPNStructure **zs,
687                                        ODR odr,
688                                        int *error_code, const char **addinfo)
689 {
690     while (ptr && ptr->type != XML_ELEMENT_NODE)
691         ptr = ptr->next;
692     
693     if (!ptr || ptr->type != XML_ELEMENT_NODE)
694     {
695         *error_code = 1;
696         *addinfo = "missing rpn operator, rset, apt node";
697         return;
698     }
699     if (check_diagnostic(ptr, odr, error_code, addinfo))
700         return;
701
702     *zs = (Z_RPNStructure *) odr_malloc(odr, sizeof(Z_RPNStructure));
703     if (!xmlStrcmp(ptr->name, BAD_CAST "operator"))
704     {
705         Z_Complex *zc = (Z_Complex *) odr_malloc(odr, sizeof(Z_Complex));
706         
707         (*zs)->which = Z_RPNStructure_complex;
708         (*zs)->u.complex = zc;
709         
710         yaz_xml2query_operator(ptr, &zc->roperator, odr, error_code, addinfo);
711
712         ptr = ptr->children;
713         while (ptr && ptr->type != XML_ELEMENT_NODE)
714             ptr = ptr->next;
715         yaz_xml2query_rpnstructure(ptr, &zc->s1, odr, error_code, addinfo);
716         if (ptr)
717             ptr = ptr->next;
718         while (ptr && ptr->type != XML_ELEMENT_NODE)
719             ptr = ptr->next;
720         yaz_xml2query_rpnstructure(ptr, &zc->s2, odr, error_code, addinfo);
721     }
722     else 
723     {
724         Z_Operand *s = (Z_Operand *) odr_malloc(odr, sizeof(Z_Operand));
725         (*zs)->which = Z_RPNStructure_simple;
726         (*zs)->u.simple = s;
727         if (!xmlStrcmp(ptr->name, BAD_CAST "apt"))
728         {
729             s->which = Z_Operand_APT;
730             yaz_xml2query_apt(ptr, &s->u.attributesPlusTerm,
731                               odr, error_code, addinfo);
732         }
733         else if (!xmlStrcmp(ptr->name, BAD_CAST "rset"))
734         {
735             s->which = Z_Operand_resultSetId; 
736             yaz_xml2query_rset(ptr, &s->u.resultSetId,
737                                odr, error_code, addinfo);
738         }
739         else
740         {
741             *error_code = 1;
742             *addinfo = "bad element: expected binary, apt or rset";
743         }        
744     }
745 }
746
747 static void yaz_xml2query_rpn(const xmlNode *ptr, Z_RPNQuery **query, ODR odr,
748                               int *error_code, const char **addinfo)
749 {
750     xmlChar *set = xmlGetProp((xmlNodePtr) ptr, BAD_CAST "set");
751
752     *query = (Z_RPNQuery*) odr_malloc(odr, sizeof(Z_RPNQuery));
753     if (set)
754     {
755         (*query)->attributeSetId =
756             yaz_string_to_oid_odr(yaz_oid_std(),
757                                   CLASS_ATTSET, (const char *) set, odr);
758         xmlFree(set);
759     }
760     else
761         (*query)->attributeSetId = 0;
762     yaz_xml2query_rpnstructure(ptr->children, &(*query)->RPNStructure,
763                                odr, error_code, addinfo);
764 }
765
766 static void yaz_xml2query_(const xmlNode *ptr, Z_Query **query, ODR odr,
767                            int *error_code, const char **addinfo)
768 {
769     if (check_diagnostic(ptr, odr, error_code, addinfo))
770         return;
771     if (ptr && ptr->type == XML_ELEMENT_NODE && 
772         !xmlStrcmp(ptr->name, BAD_CAST "query"))
773     {
774         const char *type;
775         ptr = ptr->children;
776         while (ptr && ptr->type != XML_ELEMENT_NODE)
777             ptr = ptr->next;
778         if (!ptr || ptr->type != XML_ELEMENT_NODE)
779         {
780             *error_code = 1;
781             *addinfo = "missing query content";
782             return;
783         }
784         type = (const char *) ptr->name;
785
786         *query = (Z_Query*) odr_malloc(odr, sizeof(Z_Query));
787         if (!type || !strcmp(type, "rpn"))
788         {
789             (*query)->which = Z_Query_type_1;
790             yaz_xml2query_rpn(ptr, &(*query)->u.type_1, odr,
791                               error_code, addinfo);
792         }
793         else if (!strcmp(type, "ccl"))
794         {
795             *error_code = 1;
796             *addinfo = "ccl not supported yet";
797         }
798         else if (!strcmp(type, "z39.58"))
799         {
800             *error_code = 1;
801             *addinfo = "z39.58 not supported yet";
802         }
803         else if (!strcmp(type, "cql"))
804         {
805             *error_code = 1;
806             *addinfo = "cql not supported yet";
807         }
808         else
809         {
810             *error_code = 1;
811             *addinfo = "unsupported query type";
812         }
813     }
814     else
815     {
816         *error_code = 1;
817         *addinfo = "missing query element";
818     }
819 }
820
821 void yaz_xml2query(const xmlNode *xmlnodep, Z_Query **query, ODR odr,
822                    int *error_code, const char **addinfo)
823 {
824     yaz_xml2query_(xmlnodep, query, odr, error_code, addinfo);
825 }
826
827 /* YAZ_HAVE_XML2 */
828 #endif
829
830 /*
831  * Local variables:
832  * c-basic-offset: 4
833  * c-file-style: "Stroustrup"
834  * indent-tabs-mode: nil
835  * End:
836  * vim: shiftwidth=4 tabstop=8 expandtab
837  */
838