Added last_child field to the node.
[yaz-moved-to-github.git] / retrieval / d1_map.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_map.c,v $
7  * Revision 1.4  1995-12-11 15:22:37  quinn
8  * Added last_child field to the node.
9  * Rewrote schema-mapping.
10  *
11  * Revision 1.3  1995/11/01  16:34:56  quinn
12  * Making data1 look for tables in data1_tabpath
13  *
14  * Revision 1.2  1995/11/01  13:54:46  quinn
15  * Minor adjustments
16  *
17  * Revision 1.1  1995/11/01  11:56:08  quinn
18  * Added Retrieval (data management) functions en masse.
19  *
20  *
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <oid.h>
28 #include <xmalloc.h>
29 #include <log.h>
30 #include <readconf.h>
31
32 #include <tpath.h>
33 #include <data1.h>
34 #include "d1_map.h"
35
36 data1_maptab *data1_read_maptab(char *file)
37 {
38     data1_maptab *res = xmalloc(sizeof(*res));
39     FILE *f;
40     int argc;
41     char *argv[50], line[512];
42     data1_mapunit **mapp;
43
44     if (!(f = yaz_path_fopen(data1_tabpath, file, "r")))
45     {
46         logf(LOG_WARN|LOG_ERRNO, "%s", file);
47         return 0;
48     }
49
50     res->name = 0;
51     res->target_absyn_ref = ODR_NONE;
52     res->map = 0;
53     mapp = &res->map;
54     res->next = 0;
55
56     while ((argc = readconf_line(f, line, 512, argv, 50)))
57         if (!strcmp(argv[0], "targetref"))
58         {
59             if (argc != 2)
60             {
61                 logf(LOG_WARN, "%s: one argument required for targetref",
62                     file);
63                 continue;
64             }
65             if ((res->target_absyn_ref = oid_getvalbyname(argv[1])) == ODR_NONE)
66             {
67                 logf(LOG_WARN, "%s: Unknown reference '%s'", file, argv[1]);
68                 continue;
69             }
70         }
71         else if (!strcmp(argv[0], "targetname"))
72         {
73             if (argc != 2)
74             {
75                 logf(LOG_WARN, "%s: one argument required for targetref",
76                     file);
77                 continue;
78             }
79             res->target_absyn_name = xmalloc(strlen(argv[1])+1);
80             strcpy(res->target_absyn_name, argv[1]);
81         }
82         else if (!strcmp(argv[0], "name"))
83         {
84             if (argc != 2)
85             {
86                 logf(LOG_WARN, "%s: one argument required for name",
87                     file);
88                 continue;
89             }
90             res->name = xmalloc(strlen(argv[1])+1);
91             strcpy(res->name, argv[1]);
92         }
93         else if (!strcmp(argv[0], "map"))
94         {
95             data1_maptag **mtp;
96             char *ep, *path = argv[2];
97
98             if (argc < 3)
99             {
100                 logf(LOG_WARN, "%s: At least 2 arguments required for map",
101                     file);
102                 continue;
103             }
104             *mapp = xmalloc(sizeof(**mapp));
105             (*mapp)->next = 0;
106             if (argc > 3 && !data1_matchstr(argv[3], "nodata"))
107                 (*mapp)->no_data = 1;
108             else
109                 (*mapp)->no_data = 0;
110             (*mapp)->source_element_name = xmalloc(strlen(argv[1])+1);
111             strcpy((*mapp)->source_element_name, argv[1]);
112             mtp = &(*mapp)->target_path;
113             if (*path == '/')
114                 path++;
115             for (ep = strchr(path, '/'); path; (void)((path = ep) &&
116                 (ep = strchr(path, '/'))))
117             {
118                 int type, np;
119                 char valstr[512], parm[512];
120
121                 if (ep)
122                     ep++;
123                 if ((np = sscanf(path, "(%d,%[^)]):%[^/]", &type, valstr,
124                     parm)) < 2)
125                 {
126                     logf(LOG_WARN, "%s: Syntax error in map directive: %s",
127                         file, argv[2]);
128                     fclose(f);
129                     return 0;
130                 }
131                 *mtp = xmalloc(sizeof(**mtp));
132                 (*mtp)->next = 0;
133                 (*mtp)->type = type;
134                 if (np > 2 && !data1_matchstr(parm, "new"))
135                     (*mtp)->new_field = 1;
136                 else
137                     (*mtp)->new_field = 0;
138 #if 0
139                 if ((numval = atoi(valstr)))
140                 {
141                     (*mtp)->which = D1_MAPTAG_numeric;
142                     (*mtp)->value.numeric = numval;
143                 }
144                 else
145                 {
146 #endif
147                     (*mtp)->which = D1_MAPTAG_string;
148                     (*mtp)->value.string = xmalloc(strlen(valstr)+1);
149                     strcpy((*mtp)->value.string, valstr);
150 #if 0
151                 }
152 #endif 
153                 mtp = &(*mtp)->next;
154             }
155             mapp = &(*mapp)->next;
156         }
157         else 
158             logf(LOG_WARN, "%s: Unknown directive '%s'", argv[0]);
159
160     fclose(f);
161     return res;
162 }
163
164 #if 0
165
166 /*
167  * Locate node with givel elementname.
168  * NOTE: This is stupid - we don't find repeats this way.
169  */
170 static data1_node *find_node(data1_node *p, char *elementname)
171 {
172     data1_node *c, *r;
173
174     for (c = p->child; c; c = c->next)
175         if (c->which == DATA1N_tag && c->u.tag.element &&
176             !data1_matchstr(c->u.tag.element->name, elementname))
177             return c;
178         else if ((r = find_node(c, elementname)))
179             return r;
180     return 0;
181 }
182
183 /*
184  * See if the node n is equivalent to the tag t.
185  */
186 static int tagmatch(data1_node *n, data1_maptag *t)
187 {
188     if (n->which != DATA1N_tag)
189         return 0;
190     if (n->u.tag.element)
191     {
192         if (n->u.tag.element->tag->tagset->type != t->type)
193             return 0;
194         if (n->u.tag.element->tag->which == DATA1T_numeric)
195         {
196             if (t->which != D1_MAPTAG_numeric)
197                 return 0;
198             if (n->u.tag.element->tag->value.numeric != t->value.numeric)
199                 return 0;
200         }
201         else
202         {
203             if (t->which != D1_MAPTAG_string)
204                 return 0;
205             if (data1_matchstr(n->u.tag.element->tag->value.string,
206                 t->value.string))
207                 return 0;
208         }
209     }
210     else /* local tag */
211     {
212         char str[10];
213
214         if (t->type != 3)
215             return 0;
216         if (t->which == D1_MAPTAG_numeric)
217             sprintf(str, "%d", t->value.numeric);
218         else
219             strcpy(str, t->value.string);
220         if (data1_matchstr(n->u.tag.tag, str))
221             return 0;
222     }
223     return 1;
224 }
225
226 static int map_children(data1_node *n, data1_maptab *map, data1_node *res)
227 {
228     data1_node *c;
229     data1_mapunit *m;
230     /*
231      * locate each source element in turn.
232      */
233     for (c = n->child; c; c = c->next)
234         if (c->which == DATA1N_tag && c->u.tag.element)
235         {
236             for (m = map->map; m; m = m->next)
237             {
238                 if (!data1_matchstr(m->source_element_name,
239                     c->u.tag.element->name))
240                 {
241                     data1_node *pn = res;
242                     data1_maptag *mt;
243                     data1_node *l;
244
245                     /*
246                      * process the target path specification.
247                      */
248                     for (mt = m->target_path; mt; mt = mt->next)
249                     {
250                         int match = 0;
251                         data1_node *cur;
252                         data1_node *last;
253
254                         for (l = pn->child, last = 0; l; last = l, l = l->next)
255                             if (!match)
256                                 match = tagmatch(l, mt);
257                             else
258                                 if (!tagmatch(l, mt))
259                                     break;
260                         if (!match || !mt->next || mt->new_field)
261                         {
262                             cur = data1_mk_node();
263                             cur->which = DATA1N_tag;
264                             cur->u.tag.element = 0;
265                             cur->u.tag.tag = mt->value.string;
266                             cur->u.tag.node_selected = 0;
267                             cur->parent = pn;
268                             cur->root = pn->root;
269                             if (!last)
270                             {
271                                 cur->next = pn->child;
272                                 pn->child = cur;
273                             }
274                             else
275                             {
276                                 cur->next = last->next;
277                                 last->next = cur;
278                             }
279                             pn->num_children++;
280                         }
281                         else
282                             cur = last ? last : pn->child;
283                         
284                         if (mt ->next)
285                             pn = cur;
286                         else if (!m->no_data)
287                         {
288                             cur->child = c->child;
289                             cur->num_children = c->num_children;
290                             c->child = 0;
291                             c->num_children = 0;
292                         }
293                     }
294                 }
295             }
296             if (map_children(c, map, res) < 0)
297                 return -1;
298         }
299     return 0;
300 }
301
302
303 #else
304
305 /*
306  * See if the node n is equivalent to the tag t.
307  */
308 static int tagmatch(data1_node *n, data1_maptag *t)
309 {
310     if (n->which != DATA1N_tag)
311         return 0;
312     if (n->u.tag.element)
313     {
314         if (n->u.tag.element->tag->tagset->type != t->type)
315             return 0;
316         if (n->u.tag.element->tag->which == DATA1T_numeric)
317         {
318             if (t->which != D1_MAPTAG_numeric)
319                 return 0;
320             if (n->u.tag.element->tag->value.numeric != t->value.numeric)
321                 return 0;
322         }
323         else
324         {
325             if (t->which != D1_MAPTAG_string)
326                 return 0;
327             if (data1_matchstr(n->u.tag.element->tag->value.string,
328                 t->value.string))
329                 return 0;
330         }
331     }
332     else /* local tag */
333     {
334         char str[10];
335
336         if (t->type != 3)
337             return 0;
338         if (t->which == D1_MAPTAG_numeric)
339             sprintf(str, "%d", t->value.numeric);
340         else
341             strcpy(str, t->value.string);
342         if (data1_matchstr(n->u.tag.tag, str))
343             return 0;
344     }
345     return 1;
346 }
347
348 static int map_elements(data1_node *res, data1_node *n, data1_mapunit *m)
349 {
350     data1_node *c;
351
352     for (c = n->child; c; c = c->next)
353     {
354         if (c->which == DATA1N_tag)
355         {
356             if (c->u.tag.element && !data1_matchstr(c->u.tag.element->name,
357                 m->source_element_name))
358             {
359                 /* Process target path specification */
360                 data1_maptag *mt;
361                 data1_node *pn = res, *cur = pn->last_child;
362
363                 for (mt = m->target_path; mt; mt = mt->next)
364                 {
365                     if (!cur || !tagmatch(cur, mt))
366                     {
367                         cur = data1_mk_node();
368                         cur->which = DATA1N_tag;
369                         cur->u.tag.element = 0;
370                         cur->u.tag.tag = mt->value.string;
371                         cur->u.tag.node_selected = 0;
372                         cur->parent = pn;
373                         cur->root = pn->root;
374                         if (!pn->child)
375                             pn->child = cur;
376                         if (pn->last_child)
377                             pn->last_child->next = cur;
378                         pn->last_child = cur;
379                         pn->num_children++;
380                     }
381                     if (mt->next)
382                         pn = cur;
383                     else if (!m->no_data)
384                     {
385                         cur->child = c->child;
386                         cur->num_children = c->num_children;
387                         c->child = 0;
388                         c->num_children = 0;
389                     }
390                 }
391             }
392             else if (map_elements(res, c, m) < 0)
393                 return -1;
394         }
395     }
396     return 0;
397 }
398
399 static int map_record(data1_node *res, data1_node *n, data1_maptab *map)
400 {
401     data1_mapunit *m;
402
403     for (m = map->map; m; m = m->next)
404         if (map_elements(res, n, m) < 0)
405             return -1;
406     return 0;
407 }
408
409 /*
410  * Create a (possibly lossy) copy of the given record based on the
411  * table. The new copy will refer back to the data of the original record,
412  * which should not be discarded during the lifetime of the copy.
413  */
414 data1_node *data1_map_record(data1_node *n, data1_maptab *map)
415 {
416     data1_node *res = data1_mk_node();
417
418     res->which = DATA1N_root;
419     res->u.root.type = map->target_absyn_name;
420     if (!(res->u.root.absyn = data1_get_absyn(map->target_absyn_name)))
421     {
422         logf(LOG_WARN, "%s: Failed to load target absyn '%s'",
423             map->name, map->target_absyn_name);
424     }
425     res->parent = 0;
426     res->root = res;
427
428     if (map_record(res, n, map) < 0)
429     {
430         data1_free_tree(res);
431         return 0;
432     }
433     return res;
434 }
435
436 #endif