Reorder declaration.
[idzebra-moved-to-github.git] / recctrl / xslt.c
1 /* $Id: xslt.c,v 1.5 2005-05-01 07:38:51 adam Exp $
2    Copyright (C) 1995-2005
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <ctype.h>
26
27 #include <yaz/diagbib1.h>
28 #include <libxml/xmlversion.h>
29 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31 #ifdef LIBXML_READER_ENABLED
32 #include <libxml/xmlreader.h>
33 #endif
34 #include <libxslt/transform.h>
35
36 #include <idzebra/util.h>
37 #include <idzebra/recctrl.h>
38
39 struct filter_info {
40     xsltStylesheetPtr stylesheet_xsp;
41 #ifdef LIBXML_READER_ENABLED
42     xmlTextReaderPtr reader;
43 #endif
44     char *fname;
45     int split_depth;
46     ODR odr;
47 };
48
49 #define ZEBRA_INDEX_NS "http://indexdata.dk/zebra/indexing/1"
50 #define ZEBRA_SCHEMA_IDENTITY_NS "http://indexdata.dk/zebra/identity/1"
51 static const char *zebra_index_ns = ZEBRA_INDEX_NS;
52
53 static void set_param_str(const char **params, const char *name,
54                           const char *value, ODR odr)
55 {
56     char *quoted = odr_malloc(odr, 3 + strlen(value));
57     sprintf(quoted, "'%s'", value);
58     while (*params)
59         params++;
60     params[0] = name;
61     params[1] = quoted;
62     params[2] = 0;
63 }
64
65 static void set_param_int(const char **params, const char *name,
66                           zint value, ODR odr)
67 {
68     char *quoted = odr_malloc(odr, 30); /* 25 digits enough for 2^64 */
69     while (*params)
70         params++;
71     sprintf(quoted, "'" ZINT_FORMAT "'", value);
72     params[0] = name;
73     params[1] = quoted;
74     params[2] = 0;
75 }
76
77
78 static void *filter_init_xslt(Res res, RecType recType)
79 {
80     struct filter_info *tinfo = (struct filter_info *) xmalloc(sizeof(*tinfo));
81     tinfo->stylesheet_xsp = 0;
82 #ifdef LIBXML_READER_ENABLED
83     tinfo->reader = 0;
84 #endif
85     tinfo->fname = 0;
86     tinfo->split_depth = 0;
87     tinfo->odr = odr_createmem(ODR_ENCODE);
88     return tinfo;
89 }
90
91 static void *filter_init_xslt1(Res res, RecType recType)
92 {
93     struct filter_info *tinfo = (struct filter_info *)
94         filter_init_xslt(res, recType);
95     tinfo->split_depth = 1;
96     return tinfo;
97 }
98
99 static void filter_config(void *clientData, Res res, const char *args)
100 {
101     struct filter_info *tinfo = clientData;
102     if (!args || !*args)
103         args = "default.xsl";
104     if (!tinfo->fname || strcmp(args, tinfo->fname))
105     {
106         /* different filename so must reread stylesheet */
107         xfree(tinfo->fname);
108         tinfo->fname = xstrdup(args);
109         if (tinfo->stylesheet_xsp)
110             xsltFreeStylesheet(tinfo->stylesheet_xsp);
111         tinfo->stylesheet_xsp =
112             xsltParseStylesheetFile((const xmlChar*) tinfo->fname);
113     }
114 }
115
116 static void filter_destroy(void *clientData)
117 {
118     struct filter_info *tinfo = clientData;
119     if (tinfo->stylesheet_xsp)
120         xsltFreeStylesheet(tinfo->stylesheet_xsp);
121 #ifdef LIBXML_READER_ENABLED
122     if (tinfo->reader)
123         xmlFreeTextReader(tinfo->reader);
124 #endif
125     xfree(tinfo->fname);
126     odr_destroy(tinfo->odr);
127     xfree(tinfo);
128 }
129
130 static int ioread_ex(void *context, char *buffer, int len)
131 {
132     struct recExtractCtrl *p = context;
133     return (*p->readf)(p->fh, buffer, len);
134 }
135
136 static int ioclose_ex(void *context)
137 {
138     return 0;
139 }
140
141 static void index_field(struct filter_info *tinfo, struct recExtractCtrl *ctrl,
142                         xmlNodePtr ptr, RecWord *recWord)
143 {
144     for(; ptr; ptr = ptr->next)
145     {
146         index_field(tinfo, ctrl, ptr->children, recWord);
147         if (ptr->type != XML_TEXT_NODE)
148             continue;
149         recWord->term_buf = ptr->content;
150         recWord->term_len = strlen(ptr->content);
151         (*ctrl->tokenAdd)(recWord);
152     }
153 }
154
155 static void index_node(struct filter_info *tinfo,  struct recExtractCtrl *ctrl,
156                        xmlNodePtr ptr, RecWord *recWord)
157 {
158     for(; ptr; ptr = ptr->next)
159     {
160         index_node(tinfo, ctrl, ptr->children, recWord);
161         if (ptr->type != XML_ELEMENT_NODE || !ptr->ns ||
162             strcmp(ptr->ns->href, zebra_index_ns))
163             continue;
164         if (!strcmp(ptr->name, "index"))
165         {
166             char *field_str = 0;
167             const char *xpath_str = 0;
168             struct _xmlAttr *attr;
169             for (attr = ptr->properties; attr; attr = attr->next)
170             {
171                 if (!strcmp(attr->name, "field") 
172                     && attr->children && attr->children->type == XML_TEXT_NODE)
173                     field_str = attr->children->content;
174                 if (!strcmp(attr->name, "xpath") 
175                     && attr->children && attr->children->type == XML_TEXT_NODE)
176                     xpath_str = attr->children->content;
177             }
178             if (field_str)
179             {
180                 recWord->attrStr = field_str;
181                 index_field(tinfo, ctrl, ptr->children, recWord);
182             }
183         }
184     }
185 }
186
187 static int extract_doc(struct filter_info *tinfo, struct recExtractCtrl *p,
188                        xmlDocPtr doc)
189 {
190     RecWord recWord;
191     const char *params[10];
192     xmlChar *buf_out;
193     int len_out;
194
195     params[0] = 0;
196     set_param_str(params, "schema", ZEBRA_INDEX_NS, tinfo->odr);
197
198     (*p->init)(p, &recWord);
199     recWord.reg_type = 'w';
200
201     if (tinfo->stylesheet_xsp)
202     {
203         xmlDocPtr resDoc = 
204             xsltApplyStylesheet(tinfo->stylesheet_xsp,
205                                 doc, params);
206         if (p->flagShowRecords)
207         {
208             xmlDocDumpMemory(resDoc, &buf_out, &len_out);
209             fwrite(buf_out, len_out, 1, stdout);
210             xmlFree(buf_out);
211         }
212         index_node(tinfo, p, xmlDocGetRootElement(resDoc), &recWord);
213         xmlFreeDoc(resDoc);
214     }
215     xmlDocDumpMemory(doc, &buf_out, &len_out);
216     if (p->flagShowRecords)
217         fwrite(buf_out, len_out, 1, stdout);
218     (*p->setStoreData)(p, buf_out, len_out);
219     xmlFree(buf_out);
220     
221     xmlFreeDoc(doc);
222     return RECCTRL_EXTRACT_OK;
223 }
224
225 #ifdef LIBXML_READER_ENABLED
226 static int extract_split(struct filter_info *tinfo, struct recExtractCtrl *p)
227 {
228     int ret;
229     if (p->first_record)
230     {
231         if (tinfo->reader)
232             xmlFreeTextReader(tinfo->reader);
233         tinfo->reader = xmlReaderForIO(ioread_ex, ioclose_ex,
234                                        p /* I/O handler */,
235                                        0 /* URL */, 
236                                        0 /* encoding */,
237                                        XML_PARSE_XINCLUDE);
238     }
239     if (!tinfo->reader)
240         return RECCTRL_EXTRACT_ERROR_GENERIC;
241
242     if (!tinfo->stylesheet_xsp)
243         return RECCTRL_EXTRACT_ERROR_GENERIC;
244
245     ret = xmlTextReaderRead(tinfo->reader);
246     while (ret == 1) {
247         int type = xmlTextReaderNodeType(tinfo->reader);
248         int depth = xmlTextReaderDepth(tinfo->reader);
249         if (tinfo->split_depth == 0 ||
250             (type == XML_READER_TYPE_ELEMENT && tinfo->split_depth == depth))
251         {
252             xmlNodePtr ptr = xmlTextReaderExpand(tinfo->reader);
253             xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
254             xmlDocPtr doc = xmlNewDoc("1.0");
255
256             xmlDocSetRootElement(doc, ptr2);
257
258             return extract_doc(tinfo, p, doc);      
259         }
260         ret = xmlTextReaderRead(tinfo->reader);
261     }
262     xmlFreeTextReader(tinfo->reader);
263     tinfo->reader = 0;
264     return RECCTRL_EXTRACT_EOF;
265 }
266 #endif
267
268 static int extract_full(struct filter_info *tinfo, struct recExtractCtrl *p)
269 {
270     if (p->first_record) /* only one record per stream */
271     {
272         xmlDocPtr doc = xmlReadIO(ioread_ex, ioclose_ex, p /* I/O handler */,
273                                   0 /* URL */,
274                                   0 /* encoding */,
275                                   XML_PARSE_XINCLUDE);
276         if (!doc)
277         {
278             return RECCTRL_EXTRACT_ERROR_GENERIC;
279         }
280         return extract_doc(tinfo, p, doc);
281     }
282     else
283         return RECCTRL_EXTRACT_EOF;
284 }
285
286 static int filter_extract(void *clientData, struct recExtractCtrl *p)
287 {
288     struct filter_info *tinfo = clientData;
289
290     odr_reset(tinfo->odr);
291
292     if (tinfo->split_depth == 0)
293         return extract_full(tinfo, p);
294     else
295     {
296 #ifdef LIBXML_READER_ENABLED
297         return extract_split(tinfo, p);
298 #else
299         /* no xmlreader so we can't split it */
300         return RECCTRL_EXTRACT_ERROR_GENERIC;
301 #endif
302     }
303 }
304
305 static int ioread_ret(void *context, char *buffer, int len)
306 {
307     struct recRetrieveCtrl *p = context;
308     return (*p->readf)(p->fh, buffer, len);
309 }
310
311 static int ioclose_ret(void *context)
312 {
313     return 0;
314 }
315
316 static int filter_retrieve (void *clientData, struct recRetrieveCtrl *p)
317 {
318     const char *esn = ZEBRA_SCHEMA_IDENTITY_NS;
319     const char *params[10];
320     struct filter_info *tinfo = clientData;
321     xmlDocPtr resDoc;
322     xmlDocPtr doc;
323
324     if (p->comp)
325     {
326         if (p->comp->which != Z_RecordComp_simple
327             || p->comp->u.simple->which != Z_ElementSetNames_generic)
328         {
329             p->diagnostic = YAZ_BIB1_PRESENT_COMP_SPEC_PARAMETER_UNSUPP;
330             return 0;
331         }
332         esn = p->comp->u.simple->u.generic;
333     }
334     
335     params[0] = 0;
336     set_param_str(params, "schema", esn, p->odr);
337     if (p->fname)
338         set_param_str(params, "filename", p->fname, p->odr);
339     if (p->score >= 0)
340         set_param_int(params, "score", p->score, p->odr);
341     set_param_int(params, "size", p->recordSize, p->odr);
342     
343     if (!tinfo->stylesheet_xsp)
344     {
345         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
346         return 0;
347     }
348     doc = xmlReadIO(ioread_ret, ioclose_ret, p /* I/O handler */,
349                     0 /* URL */,
350                     0 /* encoding */,
351                     XML_PARSE_XINCLUDE);
352     if (!doc)
353     {
354         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
355         return 0;
356     }
357
358     if (!strcmp(esn, ZEBRA_SCHEMA_IDENTITY_NS))
359         resDoc = doc;
360     else
361     {
362         resDoc = xsltApplyStylesheet(tinfo->stylesheet_xsp,
363                                      doc, params);
364         xmlFreeDoc(doc);
365     }
366     if (!resDoc)
367     {
368         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
369     }
370     else if (p->input_format == VAL_NONE || p->input_format == VAL_TEXT_XML)
371     {
372         xmlChar *buf_out;
373         int len_out;
374         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
375
376         p->output_format = VAL_TEXT_XML;
377         p->rec_len = len_out;
378         p->rec_buf = odr_malloc(p->odr, p->rec_len);
379         memcpy(p->rec_buf, buf_out, p->rec_len);
380         
381         xmlFree(buf_out);
382     }
383     else if (p->output_format == VAL_SUTRS)
384     {
385         xmlChar *buf_out;
386         int len_out;
387         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
388
389         p->output_format = VAL_SUTRS;
390         p->rec_len = len_out;
391         p->rec_buf = odr_malloc(p->odr, p->rec_len);
392         memcpy(p->rec_buf, buf_out, p->rec_len);
393         
394         xmlFree(buf_out);
395     }
396     else
397     {
398         p->diagnostic = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
399     }
400     xmlFreeDoc(resDoc);
401     return 0;
402 }
403
404 static struct recType filter_type_xslt = {
405     0,
406     "xslt",
407     filter_init_xslt,
408     filter_config,
409     filter_destroy,
410     filter_extract,
411     filter_retrieve
412 };
413
414 static struct recType filter_type_xslt1 = {
415     0,
416     "xslt1",
417     filter_init_xslt1,
418     filter_config,
419     filter_destroy,
420     filter_extract,
421     filter_retrieve
422 };
423
424 RecType
425 #ifdef IDZEBRA_STATIC_XSLT
426 idzebra_filter_xslt
427 #else
428 idzebra_filter
429 #endif
430
431 [] = {
432     &filter_type_xslt,
433 #ifdef LIBXML_READER_ENABLED
434     &filter_type_xslt1,
435 #endif
436     0,
437 };