1 from constants
import XSLT_NS
2 from xml
.dom
import Node
4 from Ft
.Xml
.Xslt
.StylesheetReader
import StylesheetReader
5 from Ft
.Xml
.Xslt
.StylesheetTree
import XsltElement
, XsltText
6 from Ft
.Xml
.Xslt
.LiteralElement
import LiteralElement
7 from Ft
.Xml
.Xslt
.ApplyTemplatesElement
import ApplyTemplatesElement
8 from Ft
.Xml
.Xslt
.ValueOfElement
import ValueOfElement
9 from Ft
.Xml
.Xslt
.CopyElement
import CopyElement
10 from Program
import Program
, Op
, Block
12 def mode_prog_name(mode
):
14 return 'Mode:' + mode
[1]
18 def import_sheet(doc
):
21 root
= Program('XSLT')
23 # The root program puts the mark on the Result node and the cursor on the Source.
24 # It then runs the program for the default mode. There is one program for each mode, and
25 # it acts as a dispatcher. It finds a template which matches the cursor node and runs
26 # the program for that template.
28 op
= add(root
.code
.start
, 'do_search', '/xslt/Result')
29 op
= add(op
, 'mark_selection')
30 op
= add(op
, 'do_search', '/xslt/Source')
31 op
= add(op
, 'play', 'XSLT/Default mode')
33 # This program copies a text node to the output
34 prog
= Program('DefaultText')
36 op
= add(prog
.code
.start
, 'yank')
37 op
= add(op
, 'mark_switch')
38 op
= add(op
, 'put_as_child_end')
39 op
= add(op
, 'move_left')
40 op
= add(op
, 'mark_switch')
42 # To start with, the cursor is on the source document node and
43 # the mark is on the result document node.
45 # The mode program is called with:
46 # => Cursor = context node
47 # Mark = result parent (append children here)
48 # <= Cursor is undefined
51 reader
= StylesheetReader()
52 sheet
= reader
.fromDocument(doc
)
57 # sheet.matchTemplates is { mode -> { type -> { (ns, name) -> [match] for elements
60 # Each match is (pattern, axis_type, TemplateElement)
62 # The list of matches is sorted; use the first that matches. Multiple lookups
63 # may be required (eg, lookup 'html' then None (for 'node()' and '*')).
64 # Patterns like 'text()|comment()' are broken down into two match elements.
66 # XXX: Could have two modes with the same name but different namespaces...
70 for mode
in sheet
.matchTemplates
.keys():
71 mode_name
= mode_prog_name(mode
)
72 prog
= Program(mode_name
)
74 tests
= prog
.code
.start
76 types
= sheet
.matchTemplates
[mode
]
78 for type in types
.keys():
79 if type == Node
.ELEMENT_NODE
:
80 templates
= types
[type].values()
82 templates
= [types
[type]]
86 name
= pattern
.replace('/', '%')
87 temp
= Program(`i`
+ '-' + name
)
88 op
= add(temp
.code
.start
, 'mark_switch')
89 make_template(op
, t
[2])
93 if pattern
.startswith('/'):
96 pattern
= '/xslt/Source' + pattern
# XXX: Hack
97 tests
= add(tests
, 'fail_if', pattern
)
98 op
= Op(action
= ['play', temp
.get_path()])
99 tests
.link_to(op
, 'fail')
100 loose_ends
.append(op
)
101 # Now add the built-in rules
103 #print "Tidy", loose_ends
105 tests
= add(tests
, 'fail_if', 'text()')
106 op
= Op(action
= ['play', 'XSLT/DefaultText'])
107 tests
.link_to(op
, 'fail')
109 tests
= add(tests
, 'do_global', '*')
110 tests
= add(tests
, 'map', prog
.get_path())
111 tests
= add(tests
, 'mark_switch')
112 tests
= add(tests
, 'mark_switch')
113 [ op
.link_to(tests
, 'next') for op
in loose_ends
]
118 def add(op
, *action
):
119 new
= Op(action
= action
)
120 op
.link_to(new
, 'next')
123 # A template is instantiated by running its program.
125 # => Cursor = result parent (append here)
126 # Mark = context node
128 # <= Cursor is undefined
131 # Add the instructions to instantiate this template to 'op'.
132 def make_template(op
, temp
):
133 for child
in temp
.children
:
134 if isinstance(child
, XsltText
):
135 #print "Text node", child.data
136 op
= add(op
, 'add_node', 'et', child
.data
)
137 op
= add(op
, 'move_left')
139 elif isinstance(child
, LiteralElement
):
140 #print "Element", child._output_qname
141 op
= add(op
, 'add_node', 'ee', child
._output
_qname
)
142 for (qname
, namespace
, value
) in child
._output
_attrs
:
143 if len(value
._parsedParts
) != 1:
144 print "TODO: can't handle attrib", value
146 xpath
= `value
._parsedParts
[0]`
147 if value
._plainParts
:
148 # XXX: Do this propertly
149 xpath
= 'concat("%s", %s)' % (value
._plainParts
[0], xpath
)
150 print "XPath =", xpath
151 op
= add(op
, 'mark_switch')
152 op
= add(op
, 'xpath', xpath
)
153 op
= add(op
, 'mark_switch')
154 op
= add(op
, 'add_attrib', None, qname
)
155 op
= add(op
, 'put_replace')
157 op
= make_template(op
, child
)
158 op
= add(op
, 'move_left')
159 elif isinstance(child
, ApplyTemplatesElement
):
160 block
= Block(op
.parent
)
161 block
.toggle_restore()
164 sub
= add(sub
, 'mark_switch')
165 # Ugly hack... global *|text() doesn't select in document order
167 sub
= add(sub
, 'do_global', `child
._select
.expression`
)
169 sub
= add(sub
, 'select_children')
170 sub
= add(sub
, 'map', 'XSLT/' + mode_prog_name(child
._mode
))
171 sub
= add(sub
, 'mark_switch')
173 op
.link_to(block
, 'next')
175 elif isinstance(child
, ValueOfElement
):
176 op
= add(op
, 'mark_switch')
177 op
= add(op
, 'xpath', `child
._select
.expression`
)
178 op
= add(op
, 'mark_switch')
179 op
= add(op
, 'put_as_child_end')
180 op
= add(op
, 'move_left')
181 elif isinstance(child
, CopyElement
):
182 op
= add(op
, 'mark_switch')
183 op
= add(op
, 'shallow_yank')
184 op
= add(op
, 'mark_switch')
185 op
= add(op
, 'put_as_child_end')
186 op
= make_template(op
, child
)
187 op
= add(op
, 'move_left')
189 print "Unknown template type", child
, "(%s)" % child
.__class
__