1 /*
2 * Copyright (C) 2001-2003 Peter J Jones (pjones@pmade.org)
3 * Copyright (C) 2013 Vaclav Slavik <vslavik@gmail.com>
4 * All Rights Reserved
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 * 3. Neither the name of the Author nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
24 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 /**
35 @file
36
37 This file contains the implementation of the xslt::stylesheet class.
38 */
39
40 // xmlwrapp includes
41 #include "xsltwrapp/stylesheet.h"
42 #include "xmlwrapp/document.h"
43 #include "xmlwrapp/tree_parser.h"
44 #include "xmlwrapp/errors.h"
45
46 #include "result.h"
47 #include "../libxml/utility.h"
48 #include "../libxml/errors_impl.h"
49
50 // libxslt includes
51 #include <libxslt/xslt.h>
52 #include <libxslt/xsltInternals.h>
53 #include <libxslt/transform.h>
54 #include <libxslt/xsltutils.h>
55
56 // standard includes
57 #include <memory>
58 #include <string>
59 #include <vector>
60 #include <map>
61
62
63 struct xslt::stylesheet::pimpl
64 {
pimplxslt::stylesheet::pimpl65 pimpl (void) : ss_(0), errors_occured_(false) { }
66
67 xsltStylesheetPtr ss_;
68 xml::document doc_;
69 std::string get_error_message_cache_;
70 bool errors_occured_;
71 };
72
73 namespace
74 {
75
76 // implementation of xslt::result using xslt::stylesheet: we pass this object
77 // to xml::document for the documents obtained via XSLT so that some operations
78 // (currently only saving) could be done differently for them
79 class result_impl : public xslt::impl::result
80 {
81 public:
82 // We don't own the pointers given to us, their lifetime must be greater
83 // than the lifetime of this object.
result_impl(xmlDocPtr doc,xsltStylesheetPtr ss)84 result_impl(xmlDocPtr doc, xsltStylesheetPtr ss) : doc_(doc), ss_(ss) {}
85
save_to_string(std::string & s) const86 virtual void save_to_string(std::string &s) const
87 {
88 xmlChar *xml_string;
89 int xml_string_length;
90
91 if (xsltSaveResultToString(&xml_string, &xml_string_length, doc_, ss_) >= 0)
92 {
93 xml::impl::xmlchar_helper helper(xml_string);
94 if (xml_string_length) s.assign(helper.get(), xml_string_length);
95 }
96 }
97
98 virtual bool
save_to_file(const char * filename,int) const99 save_to_file(const char *filename, int /* compression_level */) const
100 {
101 return xsltSaveResultToFilename(filename, doc_, ss_, 0) >= 0;
102 }
103
104 private:
105 xmlDocPtr doc_;
106 xsltStylesheetPtr ss_;
107 };
108
109
make_vector_param(std::vector<const char * > & v,const xslt::stylesheet::param_type & p)110 void make_vector_param(std::vector<const char*> &v,
111 const xslt::stylesheet::param_type &p)
112 {
113 v.reserve(p.size());
114
115 xslt::stylesheet::param_type::const_iterator i = p.begin(), end = p.end();
116 for (; i != end; ++i)
117 {
118 v.push_back(i->first.c_str());
119 v.push_back(i->second.c_str());
120 }
121
122 v.push_back(static_cast<const char*>(0));
123 }
124
125
126 class xslt_errors_collector : public xml::impl::errors_collector
127 {
128 public:
xslt_errors_collector(xsltTransformContextPtr c)129 xslt_errors_collector(xsltTransformContextPtr c) : ctxt_(c) {}
130
on_error(const std::string & msg)131 virtual void on_error(const std::string& msg)
132 {
133 xml::impl::errors_collector::on_error(msg);
134
135 // tell the processor to stop when it gets a chance:
136 if ( ctxt_->state == XSLT_STATE_OK )
137 ctxt_->state = XSLT_STATE_STOPPED;
138 }
139
140 xsltTransformContextPtr ctxt_;
141 };
142
apply_stylesheet(xslt::stylesheet::pimpl * impl,xml::error_handler & on_error,xmlDocPtr doc,const xslt::stylesheet::param_type * p=NULL)143 xmlDocPtr apply_stylesheet(xslt::stylesheet::pimpl *impl,
144 xml::error_handler& on_error,
145 xmlDocPtr doc,
146 const xslt::stylesheet::param_type *p = NULL)
147 {
148 xsltStylesheetPtr style = impl->ss_;
149
150 std::vector<const char*> v;
151 if (p)
152 make_vector_param(v, *p);
153
154 xsltTransformContextPtr ctxt = xsltNewTransformContext(style, doc);
155 ctxt->_private = impl;
156
157 xslt_errors_collector err(ctxt);
158 xsltSetTransformErrorFunc(ctxt, &err, xml::impl::cb_messages_error);
159
160 xmlDocPtr result =
161 xsltApplyStylesheetUser(style, doc, p ? &v[0] : 0, NULL, NULL, ctxt);
162
163 xsltFreeTransformContext(ctxt);
164
165 // it's possible there was an error that didn't prevent creation of some
166 // (incorrect) document
167 if ( result && err.has_errors() )
168 {
169 xmlFreeDoc(result);
170 err.replay(on_error);
171 return NULL;
172 }
173
174 if ( !result )
175 {
176 // set generic error message if nothing more specific is known
177 if ( !err.has_errors() )
178 err.on_error("unknown XSLT transformation error");
179 err.replay(on_error);
180 return NULL;
181 }
182
183 err.replay(on_error);
184 return result;
185 }
186
187 } // end of anonymous namespace
188
189
stylesheet(const char * filename,xml::error_handler & on_error)190 xslt::stylesheet::stylesheet(const char *filename, xml::error_handler& on_error)
191 {
192 xml::document doc(filename, on_error);
193 init(doc, on_error);
194 }
195
196
stylesheet(xml::document doc,xml::error_handler & on_error)197 xslt::stylesheet::stylesheet(xml::document doc, xml::error_handler& on_error)
198 {
199 init(doc, on_error);
200 }
201
init(xml::document & doc,xml::error_handler & on_error)202 void xslt::stylesheet::init(xml::document& doc, xml::error_handler& on_error)
203 {
204 xmlDocPtr xmldoc = static_cast<xmlDocPtr>(doc.get_doc_data());
205 std::auto_ptr<pimpl> ap(pimpl_ = new pimpl);
206
207 if ( (pimpl_->ss_ = xsltParseStylesheetDoc(xmldoc)) == 0)
208 {
209 // TODO error_ can't get set yet. Need changes from libxslt first
210 on_error.on_error("unknown XSLT parser error");
211 throw xml::exception("unknown XSLT parser error");
212 }
213
214 // if we got this far, the xmldoc we gave to xsltParseStylesheetDoc is
215 // now owned by the stylesheet and will be cleaned up in our destructor.
216 doc.release_doc_data();
217 ap.release();
218 }
219
220
~stylesheet()221 xslt::stylesheet::~stylesheet()
222 {
223 if (pimpl_->ss_)
224 xsltFreeStylesheet(pimpl_->ss_);
225 delete pimpl_;
226 }
227
228
apply(const xml::document & doc,xml::document & result)229 bool xslt::stylesheet::apply(const xml::document &doc, xml::document &result)
230 {
231 xml::impl::errors_collector err;
232 if (apply(doc, result, err))
233 return true;
234 pimpl_->get_error_message_cache_ = err.print();
235 return false;
236 }
237
238
apply(const xml::document & doc,xml::document & result,const param_type & with_params)239 bool xslt::stylesheet::apply(const xml::document &doc, xml::document &result,
240 const param_type &with_params)
241 {
242 xml::impl::errors_collector err;
243 if (apply(doc, result, with_params, err))
244 return true;
245 pimpl_->get_error_message_cache_ = err.print();
246 return false;
247 }
248
249
apply(const xml::document & doc,xml::document & result,xml::error_handler & on_error)250 bool xslt::stylesheet::apply(const xml::document &doc,
251 xml::document &result,
252 xml::error_handler& on_error)
253 {
254 xmlDocPtr input = static_cast<xmlDocPtr>(doc.get_doc_data_read_only());
255 xmlDocPtr xmldoc = apply_stylesheet(pimpl_, on_error, input);
256
257 if (xmldoc)
258 {
259 result.set_doc_data_from_xslt(xmldoc, new result_impl(xmldoc, pimpl_->ss_));
260 return true;
261 }
262
263 return false;
264 }
265
266
apply(const xml::document & doc,xml::document & result,const param_type & with_params,xml::error_handler & on_error)267 bool xslt::stylesheet::apply(const xml::document &doc,
268 xml::document &result,
269 const param_type &with_params,
270 xml::error_handler& on_error)
271 {
272 xmlDocPtr input = static_cast<xmlDocPtr>(doc.get_doc_data_read_only());
273 xmlDocPtr xmldoc = apply_stylesheet(pimpl_, on_error, input, &with_params);
274
275 if (xmldoc)
276 {
277 result.set_doc_data_from_xslt(xmldoc, new result_impl(xmldoc, pimpl_->ss_));
278 return true;
279 }
280
281 return false;
282 }
283
284
apply(const xml::document & doc,xml::error_handler & on_error)285 xml::document& xslt::stylesheet::apply(const xml::document &doc,
286 xml::error_handler& on_error)
287 {
288 xmlDocPtr input = static_cast<xmlDocPtr>(doc.get_doc_data_read_only());
289 xmlDocPtr xmldoc = apply_stylesheet(pimpl_, on_error, input);
290
291 pimpl_->doc_.set_doc_data_from_xslt(xmldoc, new result_impl(xmldoc, pimpl_->ss_));
292 return pimpl_->doc_;
293 }
294
295
apply(const xml::document & doc,const param_type & with_params,xml::error_handler & on_error)296 xml::document& xslt::stylesheet::apply(const xml::document &doc,
297 const param_type &with_params,
298 xml::error_handler& on_error)
299 {
300 xmlDocPtr input = static_cast<xmlDocPtr>(doc.get_doc_data_read_only());
301 xmlDocPtr xmldoc = apply_stylesheet(pimpl_, on_error, input, &with_params);
302
303 pimpl_->doc_.set_doc_data_from_xslt(xmldoc, new result_impl(xmldoc, pimpl_->ss_));
304 return pimpl_->doc_;
305 }
306
307
get_error_message() const308 const std::string& xslt::stylesheet::get_error_message() const
309 {
310 return pimpl_->get_error_message_cache_;
311 }
312