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