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