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