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