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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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): 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 also be specified as a dictionary. 44 """ 45 46 self.references = references or {} 47 self.parameters = parameters or {} 48 49 # Remember the stylesheet documents. 50 51 self.stylesheets = [] 52 for filename in filenames: 53 #doc = libxml2dom.macrolib.parseFile(filename) 54 #self.stylesheets.append(libxsltmod.xsltParseStylesheetDoc(doc)) 55 self.stylesheets.append(libxsltmod.xsltParseStylesheetFile(filename)) 56 57 def __del__(self): 58 59 """ 60 Tidy up the stylesheet documents. 61 """ 62 63 for stylesheet in self.stylesheets: 64 libxsltmod.xsltFreeStylesheet(stylesheet) 65 66 def send_output(self, stream, encoding, document): 67 68 """ 69 Send output to the given 'stream' using the given output encoding for 70 the given 'document'. 71 """ 72 73 # Where an encoding is specified, get an libxml2dom document and 74 # serialise it. 75 # NOTE: This is not satisfactory where indented output is desired. 76 77 if encoding: 78 result = self.get_result(document) 79 stream.write(result.toString(encoding)) 80 return 81 82 # Otherwise, get a libxml2mod document and use the libxsltmod API. 83 84 result = self._get_result(document) 85 if result is None: 86 raise OutputError, "Transformation failed." 87 88 # Attempt to get the correctly formatted document. 89 90 if hasattr(stream, "fileno"): 91 fd = stream.fileno() 92 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 93 else: 94 fd, name = tempfile.mkstemp() 95 str_result = libxsltmod.xsltSaveResultToFd(fd, result, self.stylesheets[-1]) 96 f = os.fdopen(fd) 97 f.seek(0) 98 stream.write(f.read()) 99 f.close() 100 os.remove(name) 101 102 def get_result(self, document): 103 104 """ 105 Return a transformed document produced from the object's stylesheets and 106 the given 'document'. 107 """ 108 109 result = self._get_result(document) 110 111 if result is not None: 112 return libxml2dom.adoptNodes([result])[0] 113 else: 114 raise OutputError, "Transformation failed." 115 116 def _get_result(self, document): 117 118 """ 119 Return a transformation of the given 'document'. 120 """ 121 122 if hasattr(document, "as_native_node"): 123 document = document.as_native_node() 124 125 # Transform the localised instance into the final output. 126 127 parameters = {} 128 for name, reference in self.references.items(): 129 parameters[name.encode("utf-8")] = ("document('%s')" % self._quote(reference)).encode("utf-8") 130 for name, parameter in self.parameters.items(): 131 parameters[name.encode("utf-8")] = ("'%s'" % self._quote(parameter)).encode("utf-8") 132 #print "**", repr(parameters) 133 134 last_result = document 135 for stylesheet in self.stylesheets: 136 result = libxsltmod.xsltApplyStylesheet(stylesheet, last_result, parameters) 137 if last_result is not None: 138 last_result = result 139 else: 140 raise OutputError, "Transformation failed." 141 142 return result 143 144 def _quote(self, s): 145 146 "Make the given parameter string 's' palatable for libxslt." 147 148 return s.replace("'", "%27") 149 150 # vim: tabstop=4 expandtab shiftwidth=4