1 | # -*- coding: utf-8 -*- |
---|
2 | # |
---|
3 | # Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de> |
---|
4 | # Copyright (C) 2007-2010 Edgewall Software |
---|
5 | # All rights reserved. |
---|
6 | # |
---|
7 | # This software is licensed as described in the file COPYING, which |
---|
8 | # you should have received as part of this distribution. The terms |
---|
9 | # are also available at http://bitten.edgewall.org/wiki/License. |
---|
10 | |
---|
11 | """Recipe commands for XML processing.""" |
---|
12 | |
---|
13 | import logging |
---|
14 | import os |
---|
15 | |
---|
16 | try: |
---|
17 | import libxml2 |
---|
18 | import libxslt |
---|
19 | have_libxslt = True |
---|
20 | except ImportError: |
---|
21 | have_libxslt = False |
---|
22 | |
---|
23 | if not have_libxslt and os.name == 'nt': |
---|
24 | try: |
---|
25 | import win32com.client |
---|
26 | have_msxml = True |
---|
27 | except ImportError: |
---|
28 | have_msxml = False |
---|
29 | else: |
---|
30 | have_msxml = False |
---|
31 | |
---|
32 | log = logging.getLogger('bitten.build.xmltools') |
---|
33 | |
---|
34 | __docformat__ = 'restructuredtext en' |
---|
35 | |
---|
36 | def transform(ctxt, src=None, dest=None, stylesheet=None): |
---|
37 | """Apply an XSLT stylesheet to a source XML document. |
---|
38 | |
---|
39 | This command requires either libxslt (with Python bindings), or MSXML to |
---|
40 | be installed. |
---|
41 | |
---|
42 | :param ctxt: the build context |
---|
43 | :type ctxt: `Context` |
---|
44 | :param src: name of the XML input file |
---|
45 | :param dest: name of the XML output file |
---|
46 | :param stylesheet: name of the file containing the XSLT stylesheet |
---|
47 | """ |
---|
48 | assert src, 'Missing required attribute "src"' |
---|
49 | assert dest, 'Missing required attribute "dest"' |
---|
50 | assert stylesheet, 'Missing required attribute "stylesheet"' |
---|
51 | |
---|
52 | if have_libxslt: |
---|
53 | log.debug('Using libxslt for XSLT transformation') |
---|
54 | srcdoc, styledoc, result = None, None, None |
---|
55 | try: |
---|
56 | srcdoc = libxml2.parseFile(ctxt.resolve(src)) |
---|
57 | styledoc = libxslt.parseStylesheetFile(ctxt.resolve(stylesheet)) |
---|
58 | result = styledoc.applyStylesheet(srcdoc, None) |
---|
59 | styledoc.saveResultToFilename(ctxt.resolve(dest), result, 0) |
---|
60 | finally: |
---|
61 | if styledoc: |
---|
62 | styledoc.freeStylesheet() |
---|
63 | if srcdoc: |
---|
64 | srcdoc.freeDoc() |
---|
65 | if result: |
---|
66 | result.freeDoc() |
---|
67 | |
---|
68 | elif have_msxml: |
---|
69 | log.debug('Using MSXML for XSLT transformation') |
---|
70 | srcdoc = win32com.client.Dispatch('MSXML2.DOMDocument.3.0') |
---|
71 | if not srcdoc.load(ctxt.resolve(src)): |
---|
72 | err = srcdoc.parseError |
---|
73 | ctxt.error('Failed to parse XML source %s: %s' % (src, err.reason)) |
---|
74 | return |
---|
75 | styledoc = win32com.client.Dispatch('MSXML2.DOMDocument.3.0') |
---|
76 | if not styledoc.load(ctxt.resolve(stylesheet)): |
---|
77 | err = styledoc.parseError |
---|
78 | ctxt.error('Failed to parse XSLT stylesheet %s: %s' % |
---|
79 | (stylesheet, err.reason)) |
---|
80 | return |
---|
81 | result = srcdoc.transformNode(styledoc) |
---|
82 | |
---|
83 | # MSXML seems to always write produce the resulting XML document using |
---|
84 | # UTF-16 encoding, regardless of the encoding specified in the |
---|
85 | # stylesheet. For better interoperability, recode to UTF-8 here. |
---|
86 | result = result.encode('utf-8').replace(' encoding="UTF-16"?>', '?>') |
---|
87 | |
---|
88 | dest_file = file(ctxt.resolve(dest), 'w') |
---|
89 | try: |
---|
90 | dest_file.write(result) |
---|
91 | finally: |
---|
92 | dest_file.close() |
---|
93 | |
---|
94 | else: |
---|
95 | ctxt.error('No usable XSLT implementation found') |
---|
96 | |
---|
97 | # TODO: as a last resort, try to invoke 'xsltproc' to do the |
---|
98 | # transformation? |
---|