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 # Try the conventional import first. 26 27 try: 28 import libxsltmod 29 except ImportError: 30 from libxmlmods import libxsltmod 31 32 import libxml2dom 33 import os, tempfile 34 35 class OutputError(Exception): 36 pass 37 38 class Processor: 39 40 """ 41 A handler which can prepare output for an XMLTools2 template. 42 """ 43 44 def __init__(self, filenames, references=None, parameters=None, expressions=None): 45 46 """ 47 Initialise the handler with the 'filenames' of stylesheets producing the 48 final output, a 'references' dictionary indicating related stylesheets. 49 Additional 'parameters' may be specified as a dictionary whose values 50 are converted to string values; additional 'expressions' may also be 51 specified as a dictionary - such expressions are actually evaluated in 52 stylesheet. 53 """ 54 55 self.references = references or {} 56 self.parameters = parameters or {} 57 self.expressions = expressions or {} 58 59 # Remember the stylesheet documents. 60 61 self.stylesheets = [] 62 for filename in filenames: 63 #doc = libxml2dom.macrolib.parseFile(filename) 64 #self.stylesheets.append(libxsltmod.xsltParseStylesheetDoc(doc)) 65 self.stylesheets.append(libxsltmod.xsltParseStylesheetFile(filename)) 66 67 def __del__(self): 68 69 """ 70 Tidy up the stylesheet documents. 71 """ 72 73 for stylesheet in self.stylesheets: 74 libxsltmod.xsltFreeStylesheet(stylesheet) 75 76 def send_output(self, stream, encoding, document): 77 78 """ 79 Send output to the given 'stream' using the given output encoding for 80 the given 'document'. 81 """ 82 83 # Where an encoding is specified, get an libxml2dom document and 84 # serialise it. 85 # NOTE: This is not satisfactory where indented output is desired. 86 87 if encoding: 88 result = self.get_result(document) 89 stream.write(result.toString(encoding)) 90 return 91 92 # Otherwise, get a libxml2mod document and use the libxsltmod API. 93 94 result = self._get_result(document) 95 if result is None: 96 raise OutputError, "Transformation failed." 97 98 # Attempt to get the correctly formatted document. 99 100 if hasattr(stream, "fileno"): 101 stream.flush() 102 fd = stream.fileno() 103 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 104 else: 105 fd, name = tempfile.mkstemp() 106 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 107 f = os.fdopen(fd) 108 f.seek(0) 109 stream.write(f.read()) 110 f.close() 111 os.remove(name) 112 113 def get_result(self, document): 114 115 """ 116 Return a transformed document produced from the object's stylesheets and 117 the given 'document'. 118 """ 119 120 result = self._get_result(document) 121 122 if result is not None: 123 return libxml2dom.adoptNodes([result])[0] 124 else: 125 raise OutputError, "Transformation failed." 126 127 def _get_result(self, document): 128 129 """ 130 Return a transformation of the given 'document'. 131 """ 132 133 if hasattr(document, "as_native_node"): 134 document = document.as_native_node() 135 136 # Transform the localised instance into the final output. 137 138 parameters = {} 139 for name, reference in self.references.items(): 140 parameters[name.encode("utf-8")] = ("document('%s')" % self._quote(reference)).encode("utf-8") 141 for name, parameter in self.parameters.items(): 142 parameters[name.encode("utf-8")] = ("'%s'" % self._quote(parameter)).encode("utf-8") 143 for name, parameter in self.expressions.items(): 144 parameters[name.encode("utf-8")] = self._quote(parameter).encode("utf-8") 145 146 last_result = document 147 for stylesheet in self.stylesheets: 148 result = libxsltmod.xsltApplyStylesheet(stylesheet, last_result, parameters) 149 if last_result is not None: 150 last_result = result 151 else: 152 raise OutputError, "Transformation failed." 153 154 return result 155 156 def _quote(self, s): 157 158 "Make the given parameter string 's' palatable for libxslt." 159 160 return s.replace("'", "%27") 161 162 # vim: tabstop=4 expandtab shiftwidth=4