Omit CVS Id. Update copyright year.
[idzebra-moved-to-github.git] / data1 / d1_doespec.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1995-2008 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 /** \file d1_doespec.c
21  *  \brief handle Z39.50 variant-1 specs
22  *   
23  *  See http://www.loc.gov/z3950/agency/defns/variant1.html
24  */
25 #include <assert.h>
26 #include <stdlib.h>
27
28 #include <yaz/log.h>
29 #include <yaz/proto.h>
30 #include <yaz/oid_db.h>
31 #include <idzebra/data1.h>
32
33 static int match_children(data1_handle dh, data1_node *n,
34                           Z_Espec1 *e, int i, Z_ETagUnit **t,
35                           int num,
36                           int select_flag);
37
38 static int match_children_wildpath(data1_handle dh, data1_node *n,
39                                    Z_Espec1 *e, int i,
40                                    Z_ETagUnit **t, int num)
41 {
42     return 0;
43 }
44
45 /*
46  * Locate a specific triple within a variant.
47  * set is the set to look for, universal set is the set that applies to a
48  * triple with an unknown set.
49  */
50 static Z_Triple *find_triple(Z_Variant *var, const Odr_oid *universal_oid,
51                              const Odr_oid *var_oid, int zclass, int type)
52 {
53     int i;
54
55     for (i = 0; i < var->num_triples; i++)
56     {
57         const Odr_oid *cur_oid = var->triples[i]->variantSetId;
58         if (!cur_oid)
59             cur_oid = var->globalVariantSetId;
60         if (cur_oid && var_oid 
61             && !oid_oidcmp(var_oid, cur_oid) && *var->triples[i]->type == type)
62             return var->triples[i];
63     }
64     return 0;
65 }
66
67 static void mark_subtree(data1_node *n, int make_variantlist, int no_data,
68                          int get_bytes, Z_Variant *vreq, int select_flag)
69 {
70     data1_node *c;
71
72 #if 1
73     if (n->which == DATA1N_tag)
74 #else
75     if (n->which == DATA1N_tag && (!n->child || n->child->which != DATA1N_tag))
76     /*
77      * This seems to cause multi-level elements to fall out when only a
78      * top-level elementRequest has been given... Problem is, I can't figure
79      * out what it was supposed to ACHIEVE.... delete when code has been
80      * verified.
81      */
82 #endif
83     {
84         n->u.tag.node_selected = select_flag;
85         n->u.tag.make_variantlist = make_variantlist;
86         n->u.tag.no_data_requested = no_data;
87         n->u.tag.get_bytes = get_bytes;
88     }
89
90     for (c = n->child; c; c = c->next)
91     {
92         if (c->which == DATA1N_tag && (!n->child ||
93             n->child->which != DATA1N_tag))
94         {
95             c->u.tag.node_selected = select_flag;
96             c->u.tag.make_variantlist = make_variantlist;
97             c->u.tag.no_data_requested = no_data;
98             c->u.tag.get_bytes = get_bytes;
99         }
100         mark_subtree(c, make_variantlist, no_data, get_bytes, vreq,
101                      select_flag);
102     }
103 }
104
105
106 static void match_triple(data1_handle dh, Z_Variant *vreq,
107                          const Odr_oid *def_oid,
108                          const Odr_oid *var_oid, data1_node *n)
109 {
110     data1_node **c;
111
112     if (!(n = n->child))
113         return;
114     if (n->which != DATA1N_variant)
115         return;
116     c = &n->child;
117     while (*c)
118     {
119         int remove_flag = 0;
120         Z_Triple *r;
121         
122         assert ((*c)->which == DATA1N_variant);
123         
124         if ((*c)->u.variant.type->zclass->zclass == 4 &&
125             (*c)->u.variant.type->type == 1)
126         {
127             if ((r = find_triple(vreq, def_oid, var_oid, 4, 1)) &&
128                 (r->which == Z_Triple_internationalString))
129             {
130                 const char *string_value =
131                     r->value.internationalString;
132                 if (strcmp ((*c)->u.variant.value, string_value))
133                     remove_flag = 1;
134             }
135         }
136         if (remove_flag)
137         {
138             *c = (*c)->next;
139         }
140         else
141         {
142             match_triple(dh, vreq, def_oid, var_oid, *c);
143             c = &(*c)->next;
144         }
145     }
146 }
147
148 static int match_node_and_attr (data1_node *c, const char *spec)
149 {
150
151     char predicate[64];
152     char elem[64];
153     char attr[64];
154     char value[64];
155     char dummy_ch;
156
157     data1_tag *tag = 0;
158     if (c->u.tag.element)
159         tag = c->u.tag.element->tag;
160     
161     *predicate = '\0';
162     sscanf(spec, "%63[^[]%c%63[^]]", elem, &dummy_ch, predicate);
163     if (data1_matchstr(elem, tag ? tag->value.string : c->u.tag.tag))
164         return 0;
165
166     if (*predicate == '\0')
167         return 1;
168     else if (sscanf(predicate, "@%63[^=]=%63s", attr, value) == 2)
169     {
170         data1_xattr *xa;
171         for (xa = c->u.tag.attributes; xa; xa = xa->next)
172             if (!strcmp(xa->name, attr) &&
173                 !strcmp(xa->value, value))
174                 return 1;
175         return 0;
176     }
177     else if (sscanf(predicate, "@%63s", attr) == 1)
178     {
179         data1_xattr *xa;
180         for (xa = c->u.tag.attributes; xa; xa = xa->next)
181             if (!strcmp(xa->name, attr))
182                 return 1;
183     }
184     else
185     {
186         yaz_log(YLOG_WARN, "Bad simpleelement component: '%s'", spec);
187     }
188     return 0;
189 }
190                                 
191 static int match_children_here (data1_handle dh, data1_node *n,
192                                 Z_Espec1 *e, int i,
193                                 Z_ETagUnit **t, int num,
194                                 int select_flag)
195 {
196     int counter = 0, hits = 0;
197     data1_node *c;
198     Z_ETagUnit *tp = *t;
199     Z_Occurrences *occur;
200
201     for (c = n->child; c ; c = c->next)
202     {
203         data1_tag *tag = 0;
204
205         if (c->which != DATA1N_tag)
206             continue;
207
208         if (tp->which == Z_ETagUnit_specificTag)
209         {
210             Z_SpecificTag *want = tp->u.specificTag;
211             occur = want->occurrences;
212             if (c->u.tag.element)
213                 tag = c->u.tag.element->tag;
214             if (*want->tagType != ((tag && tag->tagset) ? tag->tagset->type :
215                 3))
216                 continue;
217             if (want->tagValue->which == Z_StringOrNumeric_numeric)
218             {
219                 if (!tag || tag->which != DATA1T_numeric)
220                     continue;
221                 if (*want->tagValue->u.numeric != tag->value.numeric)
222                     continue;
223             }
224             else if (want->tagValue->which == Z_StringOrNumeric_string)
225             {
226                 const char *str_val = want->tagValue->u.string;
227                 if (str_val[0] == '!')
228                 {
229                     str_val++;
230                     select_flag = 0;
231                 }
232                 if (tag && tag->which != DATA1T_string)
233                     continue;
234 #if 1
235                 if (!match_node_and_attr(c, str_val))
236                     continue;
237 #else      
238                 if (data1_matchstr(str_val,
239                                    tag ? tag->value.string : c->u.tag.tag))
240                     continue;
241 #endif
242             }
243             else
244             {
245                 yaz_log(YLOG_WARN, "Bad SpecificTag type: %d",
246                         want->tagValue->which);
247                 continue;
248             }
249         }
250         else if (tp->which == Z_ETagUnit_wildThing)
251             occur = tp->u.wildThing;
252         else
253             continue;
254         /*
255          * Ok, so we have a matching tag. Are we within occurrences-range?
256          */
257         counter++;
258         if (occur && occur->which == Z_Occurrences_last)
259         {
260             yaz_log(YLOG_WARN, "Can't do occurrences=last (yet)");
261             return 0;
262         }
263         if (!occur || occur->which == Z_Occurrences_all ||
264             (occur->which == Z_Occurrences_values && counter >=
265             *occur->u.values->start))
266         {
267             if (match_children(dh, c, e, i, t + 1, num - 1, select_flag))
268             {
269                 c->u.tag.node_selected = select_flag;
270                 /*
271                  * Consider the variant specification if this is a complete
272                  * match.
273                  */
274                 if (num == 1)
275                 {
276                     int show_variantlist = 0;
277                     int no_data = 0;
278                     int get_bytes = -1;
279
280                     Z_Variant *vreq =
281                         e->elements[i]->u.simpleElement->variantRequest;
282
283                     const Odr_oid *var_oid = yaz_oid_varset_variant_1;
284                     if (!vreq)
285                         vreq = e->defaultVariantRequest;
286
287                     if (vreq)
288                     {
289                         Z_Triple *r;
290
291                         /*
292                          * 6,5: meta-data requested, variant list.
293                          */
294                         if (find_triple(vreq, e->defaultVariantSetId, 
295                                         var_oid, 6, 5))
296                             show_variantlist = 1;
297                         /*
298                          * 9,1: Miscellaneous, no data requested.
299                          */
300                         if (find_triple(vreq, e->defaultVariantSetId,
301                                         var_oid, 9, 1))
302                             no_data = 1;
303
304                         /* howmuch */
305                         if ((r = find_triple(vreq, e->defaultVariantSetId,
306                                              var_oid, 5, 5)))
307                             if (r->which == Z_Triple_integer)
308                                 get_bytes = *r->value.integer;
309
310                         if (!show_variantlist)
311                             match_triple(dh, vreq, e->defaultVariantSetId,
312                                          var_oid, c);
313                     }
314                     mark_subtree(c, show_variantlist, no_data, get_bytes, vreq,
315                                  select_flag);
316                 }
317                 hits++;
318                 /*
319                  * have we looked at enough children?
320                  */
321                 if (!occur || (occur->which == Z_Occurrences_values &&
322                     (!occur->u.values->howMany ||
323                     counter - *occur->u.values->start >=
324                     *occur->u.values->howMany - 1)))
325                     return hits;
326             }
327         }
328     }
329     return hits;
330 }
331
332 static int match_children(data1_handle dh, data1_node *n, Z_Espec1 *e,
333                           int i, Z_ETagUnit **t, int num, int select_flag)
334 {
335     int res;
336
337     if (!num)
338         return 1;
339     switch (t[0]->which)
340     {
341     case Z_ETagUnit_wildThing:
342     case Z_ETagUnit_specificTag:
343         res = match_children_here(dh, n, e, i, t, num, select_flag);
344         break;
345     case Z_ETagUnit_wildPath:
346         res = match_children_wildpath(dh, n, e, i, t, num); break;
347     default:
348         abort();
349     }
350     return res;
351 }
352
353 int data1_doespec1 (data1_handle dh, data1_node *n, Z_Espec1 *e)
354 {
355     int i;
356
357     n = data1_get_root_tag (dh, n);
358     if (n && n->which == DATA1N_tag)
359         n->u.tag.node_selected = 1;
360     
361     for (i = 0; i < e->num_elements; i++)
362     {
363         if (e->elements[i]->which != Z_ERequest_simpleElement)
364             return 100;
365         match_children(dh, n, e, i,
366                        e->elements[i]->u.simpleElement->path->tags,
367                        e->elements[i]->u.simpleElement->path->num_tags,
368                        1 /* select (include) by default */ );
369     }
370     return 0;
371 }
372 /*
373  * Local variables:
374  * c-basic-offset: 4
375  * indent-tabs-mode: nil
376  * End:
377  * vim: shiftwidth=4 tabstop=8 expandtab
378  */
379