WS + reformat updates
[idzebra-moved-to-github.git] / recctrl / xslt.c
1 /* $Id: xslt.c,v 1.3 2005-04-28 13:33:20 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/xmlreader.h>
29 #include <libxslt/transform.h>
30
31 #include <idzebra/util.h>
32 #include <idzebra/recctrl.h>
33
34 struct filter_info {
35     xsltStylesheetPtr stylesheet_xsp;
36     xmlTextReaderPtr reader;
37     char *fname;
38     int split_depth;
39     ODR odr;
40 };
41
42 #define ZEBRA_INDEX_NS "http://indexdata.dk/zebra/indexing/1"
43 #define ZEBRA_SCHEMA_IDENTITY_NS "http://indexdata.dk/zebra/identity/1"
44 static const char *zebra_index_ns = ZEBRA_INDEX_NS;
45
46 static void set_param_str(const char **params, const char *name,
47                           const char *value, ODR odr)
48 {
49     char *quoted = odr_malloc(odr, 3 + strlen(value));
50     sprintf(quoted, "'%s'", value);
51     while (*params)
52         params++;
53     params[0] = name;
54     params[1] = quoted;
55     params[2] = 0;
56 }
57
58 static void set_param_int(const char **params, const char *name,
59                           zint value, ODR odr)
60 {
61     char *quoted = odr_malloc(odr, 30); /* 25 digits enough for 2^64 */
62     while (*params)
63         params++;
64     sprintf(quoted, "'" ZINT_FORMAT "'", value);
65     params[0] = name;
66     params[1] = quoted;
67     params[2] = 0;
68 }
69
70
71 static void *filter_init (Res res, RecType recType)
72 {
73     struct filter_info *tinfo = (struct filter_info *) xmalloc(sizeof(*tinfo));
74     tinfo->stylesheet_xsp = 0;
75     tinfo->reader = 0;
76     tinfo->fname = 0;
77     tinfo->split_depth = 1;
78     tinfo->odr = odr_createmem(ODR_ENCODE);
79     return tinfo;
80 }
81
82 static void filter_config(void *clientData, Res res, const char *args)
83 {
84     struct filter_info *tinfo = clientData;
85     if (!args || !*args)
86         args = "default.xsl";
87     if (!tinfo->fname || strcmp(args, tinfo->fname))
88     {
89         /* different filename so must reread stylesheet */
90         xfree(tinfo->fname);
91         tinfo->fname = xstrdup(args);
92         if (tinfo->stylesheet_xsp)
93             xsltFreeStylesheet(tinfo->stylesheet_xsp);
94         tinfo->stylesheet_xsp =
95             xsltParseStylesheetFile((const xmlChar*) tinfo->fname);
96     }
97 }
98
99 static void filter_destroy(void *clientData)
100 {
101     struct filter_info *tinfo = clientData;
102     if (tinfo->stylesheet_xsp)
103         xsltFreeStylesheet(tinfo->stylesheet_xsp);
104     xfree(tinfo->fname);
105     odr_destroy(tinfo->odr);
106     xfree(tinfo);
107 }
108
109 static int ioread_ex(void *context, char *buffer, int len)
110 {
111     struct recExtractCtrl *p = context;
112     return (*p->readf)(p->fh, buffer, len);
113 }
114
115 static int ioclose_ex(void *context)
116 {
117     return 0;
118 }
119
120 static void index_field(struct filter_info *tinfo, struct recExtractCtrl *ctrl,
121                         xmlNodePtr ptr, RecWord *recWord)
122 {
123     for(; ptr; ptr = ptr->next)
124     {
125         index_field(tinfo, ctrl, ptr->children, recWord);
126         if (ptr->type != XML_TEXT_NODE)
127             continue;
128         recWord->term_buf = ptr->content;
129         recWord->term_len = strlen(ptr->content);
130         (*ctrl->tokenAdd)(recWord);
131     }
132 }
133
134 static void index_node(struct filter_info *tinfo,  struct recExtractCtrl *ctrl,
135                        xmlNodePtr ptr, RecWord *recWord)
136 {
137     for(; ptr; ptr = ptr->next)
138     {
139         index_node(tinfo, ctrl, ptr->children, recWord);
140         if (ptr->type != XML_ELEMENT_NODE || !ptr->ns ||
141             strcmp(ptr->ns->href, zebra_index_ns))
142             continue;
143         if (!strcmp(ptr->name, "index"))
144         {
145             char *field_str = 0;
146             const char *xpath_str = 0;
147             struct _xmlAttr *attr;
148             for (attr = ptr->properties; attr; attr = attr->next)
149             {
150                 if (!strcmp(attr->name, "field") 
151                     && attr->children && attr->children->type == XML_TEXT_NODE)
152                     field_str = attr->children->content;
153                 if (!strcmp(attr->name, "xpath") 
154                     && attr->children && attr->children->type == XML_TEXT_NODE)
155                     xpath_str = attr->children->content;
156             }
157             if (field_str)
158             {
159                 recWord->attrStr = field_str;
160                 index_field(tinfo, ctrl, ptr->children, recWord);
161             }
162         }
163     }
164 }
165
166 static int filter_extract(void *clientData, struct recExtractCtrl *p)
167 {
168     const char *params[10];
169     struct filter_info *tinfo = clientData;
170     RecWord recWord;
171     int ret;
172
173     params[0] = 0;
174
175     odr_reset(tinfo->odr);
176     set_param_str(params, "schema", ZEBRA_INDEX_NS, tinfo->odr);
177
178     if (p->first_record)
179     {
180         if (tinfo->reader)
181             xmlFreeTextReader(tinfo->reader);
182         tinfo->reader = xmlReaderForIO(ioread_ex, ioclose_ex,
183                                        p /* I/O handler */,
184                                        0 /* URL */, 
185                                        0 /* encoding */,
186                                        XML_PARSE_XINCLUDE);
187     }
188     if (!tinfo->reader)
189         return RECCTRL_EXTRACT_ERROR_GENERIC;
190
191     if (!tinfo->stylesheet_xsp)
192         return RECCTRL_EXTRACT_ERROR_GENERIC;
193
194     (*p->init)(p, &recWord);
195     recWord.reg_type = 'w';
196
197     ret = xmlTextReaderRead(tinfo->reader);
198     while (ret == 1) {
199         int type = xmlTextReaderNodeType(tinfo->reader);
200         int depth = xmlTextReaderDepth(tinfo->reader);
201         if (tinfo->split_depth == 0 ||
202             (type == XML_READER_TYPE_ELEMENT && tinfo->split_depth == depth))
203         {
204             xmlChar *buf_out;
205             int len_out;
206
207             xmlNodePtr ptr = xmlTextReaderExpand(tinfo->reader);
208             xmlNodePtr ptr2 = xmlCopyNode(ptr, 1);
209             xmlDocPtr doc = xmlNewDoc("1.0");
210
211             xmlDocSetRootElement(doc, ptr2);
212             
213             if (tinfo->stylesheet_xsp)
214             {
215                 xmlDocPtr resDoc = 
216                     xsltApplyStylesheet(tinfo->stylesheet_xsp,
217                                         doc, params);
218                 if (p->flagShowRecords)
219                 {
220                     xmlDocDumpMemory(resDoc, &buf_out, &len_out);
221                     fwrite(buf_out, len_out, 1, stdout);
222                     xmlFree(buf_out);
223                 }
224                 index_node(tinfo, p, xmlDocGetRootElement(resDoc), &recWord);
225                 xmlFreeDoc(resDoc);
226             }
227             xmlDocDumpMemory(doc, &buf_out, &len_out);
228             if (p->flagShowRecords)
229                 fwrite(buf_out, len_out, 1, stdout);
230             (*p->setStoreData)(p, buf_out, len_out);
231             xmlFree(buf_out);
232
233             xmlFreeDoc(doc);
234             return RECCTRL_EXTRACT_OK;
235         }
236         ret = xmlTextReaderRead(tinfo->reader);
237     }
238     xmlFreeTextReader(tinfo->reader);
239     tinfo->reader = 0;
240     return RECCTRL_EXTRACT_EOF;
241 }
242
243 static int ioread_ret(void *context, char *buffer, int len)
244 {
245     struct recRetrieveCtrl *p = context;
246     return (*p->readf)(p->fh, buffer, len);
247 }
248
249 static int ioclose_ret(void *context)
250 {
251     return 0;
252 }
253
254 static int filter_retrieve (void *clientData, struct recRetrieveCtrl *p)
255 {
256     const char *esn = ZEBRA_SCHEMA_IDENTITY_NS;
257     const char *params[10];
258     struct filter_info *tinfo = clientData;
259     xmlDocPtr resDoc;
260     xmlDocPtr doc;
261
262     if (p->comp)
263     {
264         if (p->comp->which != Z_RecordComp_simple
265             || p->comp->u.simple->which != Z_ElementSetNames_generic)
266         {
267             p->diagnostic = YAZ_BIB1_PRESENT_COMP_SPEC_PARAMETER_UNSUPP;
268             return 0;
269         }
270         esn = p->comp->u.simple->u.generic;
271     }
272     
273     params[0] = 0;
274     set_param_str(params, "schema", esn, p->odr);
275     if (p->fname)
276         set_param_str(params, "filename", p->fname, p->odr);
277     if (p->score >= 0)
278         set_param_int(params, "score", p->score, p->odr);
279     set_param_int(params, "size", p->recordSize, p->odr);
280     
281     if (!tinfo->stylesheet_xsp)
282     {
283         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
284         return 0;
285     }
286     doc = xmlReadIO(ioread_ret, ioclose_ret, p /* I/O handler */,
287                     0 /* URL */,
288                     0 /* encoding */,
289                     XML_PARSE_XINCLUDE);
290     if (!doc)
291     {
292         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
293         return 0;
294     }
295
296     if (!strcmp(esn, ZEBRA_SCHEMA_IDENTITY_NS))
297         resDoc = doc;
298     else
299     {
300         resDoc = xsltApplyStylesheet(tinfo->stylesheet_xsp,
301                                      doc, params);
302         xmlFreeDoc(doc);
303     }
304     if (!resDoc)
305     {
306         p->diagnostic = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
307     }
308     else if (p->input_format == VAL_NONE || p->input_format == VAL_TEXT_XML)
309     {
310         xmlChar *buf_out;
311         int len_out;
312         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
313
314         p->output_format = VAL_TEXT_XML;
315         p->rec_len = len_out;
316         p->rec_buf = odr_malloc(p->odr, p->rec_len);
317         memcpy(p->rec_buf, buf_out, p->rec_len);
318         
319         xmlFree(buf_out);
320     }
321     else if (p->output_format == VAL_SUTRS)
322     {
323         xmlChar *buf_out;
324         int len_out;
325         xmlDocDumpMemory(resDoc, &buf_out, &len_out);
326
327         p->output_format = VAL_SUTRS;
328         p->rec_len = len_out;
329         p->rec_buf = odr_malloc(p->odr, p->rec_len);
330         memcpy(p->rec_buf, buf_out, p->rec_len);
331         
332         xmlFree(buf_out);
333     }
334     else
335     {
336         p->diagnostic = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
337     }
338     xmlFreeDoc(resDoc);
339     return 0;
340 }
341
342 static struct recType filter_type = {
343     0,
344     "xslt",
345     filter_init,
346     filter_config,
347     filter_destroy,
348     filter_extract,
349     filter_retrieve
350 };
351
352 RecType
353 #ifdef IDZEBRA_STATIC_XSLT
354 idzebra_filter_xslt
355 #else
356 idzebra_filter
357 #endif
358
359 [] = {
360     &filter_type,
361     0,
362 };