1 #!/usr/bin/env python 2 3 """ 4 XSL output classes and functions. 5 6 Copyright (C) 2005 Paul Boddie <paul@boddie.org.uk> 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Lesser General Public 10 License as published by the Free Software Foundation; either 11 version 2.1 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public 19 License along with this library; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 21 """ 22 23 # NOTE: Make this use other XSLT implementations, too. 24 25 import libxsltmod 26 import libxml2dom 27 import os, tempfile 28 29 class OutputError(Exception): 30 pass 31 32 class Processor: 33 34 """ 35 A handler which can prepare output for an XMLTools2 template. 36 """ 37 38 def __init__(self, filenames, references=None, parameters=None, expressions=None): 39 40 """ 41 Initialise the handler with the 'filenames' of stylesheets producing the 42 final output, a 'references' dictionary indicating related stylesheets. 43 Additional 'parameters' may be specified as a dictionary whose values 44 are converted to string values; additional 'expressions' may also be 45 specified as a dictionary - such expressions are actually evaluated in 46 stylesheet. 47 """ 48 49 self.references = references or {} 50 self.parameters = parameters or {} 51 self.expressions = expressions or {} 52 53 # Remember the stylesheet documents. 54 55 self.stylesheets = [] 56 for filename in filenames: 57 #doc = libxml2dom.macrolib.parseFile(filename) 58 #self.stylesheets.append(libxsltmod.xsltParseStylesheetDoc(doc)) 59 self.stylesheets.append(libxsltmod.xsltParseStylesheetFile(filename)) 60 61 def __del__(self): 62 63 """ 64 Tidy up the stylesheet documents. 65 """ 66 67 for stylesheet in self.stylesheets: 68 libxsltmod.xsltFreeStylesheet(stylesheet) 69 70 def send_output(self, stream, encoding, document): 71 72 """ 73 Send output to the given 'stream' using the given output encoding for 74 the given 'document'. 75 """ 76 77 # Where an encoding is specified, get an libxml2dom document and 78 # serialise it. 79 # NOTE: This is not satisfactory where indented output is desired. 80 81 if encoding: 82 result = self.get_result(document) 83 stream.write(result.toString(encoding)) 84 return 85 86 # Otherwise, get a libxml2mod document and use the libxsltmod API. 87 88 result = self._get_result(document) 89 if result is None: 90 raise OutputError, "Transformation failed." 91 92 # Attempt to get the correctly formatted document. 93 94 if hasattr(stream, "fileno"): 95 stream.flush() 96 fd = stream.fileno() 97 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 98 else: 99 fd, name = tempfile.mkstemp() 100 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 101 f = os.fdopen(fd) 102 f.seek(0) 103 stream.write(f.read()) 104 f.close() 105 os.remove(name) 106 107 def get_result(self, document): 108 109 """ 110 Return a transformed document produced from the object's stylesheets and 111 the given 'document'. 112 """ 113 114 result = self._get_result(document) 115 116 if result is not None: 117 return libxml2dom.adoptNodes([result])[0] 118 else: 119 raise OutputError, "Transformation failed." 120 121 def _get_result(self, document): 122 123 """ 124 Return a transformation of the given 'document'. 125 """ 126 127 if hasattr(document, "as_native_node"): 128 document = document.as_native_node() 129 130 # Transform the localised instance into the final output. 131 132 parameters = {} 133 for name, reference in self.references.items(): 134 parameters[name.encode("utf-8")] = ("document('%s')" % self._quote(reference)).encode("utf-8") 135 for name, parameter in self.parameters.items(): 136 parameters[name.encode("utf-8")] = ("'%s'" % self._quote(parameter)).encode("utf-8") 137 for name, parameter in self.expressions.items(): 138 parameters[name.encode("utf-8")] = self._quote(parameter).encode("utf-8") 139 140 last_result = document 141 for stylesheet in self.stylesheets: 142 result = libxsltmod.xsltApplyStylesheet(stylesheet, last_result, parameters) 143 if last_result is not None: 144 last_result = result 145 else: 146 raise OutputError, "Transformation failed." 147 148 return result 149 150 def _quote(self, s): 151 152 "Make the given parameter string 's' palatable for libxslt." 153 154 return s.replace("'", "%27") 155 156 # vim: tabstop=4 expandtab shiftwidth=4