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