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 ((precedence,?), 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
]
80 for type in types
.keys():
81 if type == Node
.ELEMENT_NODE
:
82 templates
= types
[type].values()[:]
84 templates
= [types
[type]]
86 # templates is a list of templates for items of this type when in this mode
94 all
.reverse() # highest numbers first
97 for sort_key
, (unused_pattern
, axis_type
, template
) in all
:
98 pattern
= `template
._match`
104 #print sort_key, pattern, axis_type, template
105 name
= pattern
.replace('/', '%')
106 temp
= Program(`i`
+ '-' + name
)
107 op
= add(temp
.code
.start
, 'mark_switch')
108 make_template(op
, template
)
112 if pattern
.startswith('/'):
115 pattern
= '/xslt/Source' + pattern
# XXX: Hack
116 tests
= add(tests
, 'xslt_fail_if', pattern
)
117 op
= Op(action
= ['play', temp
.get_path()])
118 tests
.link_to(op
, 'fail')
119 add(op
, 'mark_switch')
120 #loose_ends.append(op)
121 # Now add the built-in rules
123 #print "Tidy", loose_ends
125 tests
= add(tests
, 'xslt_fail_if', 'text()')
126 op
= Op(action
= ['play', 'XSLT/DefaultText'])
127 tests
.link_to(op
, 'fail')
129 tests
= add(tests
, 'do_global', '*')
130 tests
= add(tests
, 'map', prog
.get_path())
131 #tests = add(tests, 'mark_switch')
132 #tests = add(tests, 'mark_switch')
133 #[ op.link_to(tests, 'next') for op in loose_ends ]
138 def add(op
, *action
):
139 new
= Op(action
= action
)
140 op
.link_to(new
, 'next')
143 # A template is instantiated by running its program.
145 # => Cursor = result parent (append here)
146 # Mark = context node
148 # <= Cursor is undefined
151 # Add the instructions to instantiate this template to 'op'.
152 def make_template(op
, temp
):
153 for child
in temp
.children
:
154 if isinstance(child
, XsltText
):
155 #print "Text node", child.data
156 op
= add(op
, 'add_node', 'et', child
.data
)
157 op
= add(op
, 'move_left')
159 elif isinstance(child
, LiteralElement
):
160 #print "Element", child._output_qname
161 op
= add(op
, 'add_node', 'ee', child
._output
_qname
)
162 for (qname
, namespace
, value
) in child
._output
_attrs
:
163 if len(value
._parsedParts
) != 1:
164 print "TODO: can't handle attrib", value
166 xpath
= `value
._parsedParts
[0]`
167 if value
._plainParts
:
168 # XXX: Do this propertly
169 xpath
= 'concat("%s", %s)' % (value
._plainParts
[0], xpath
)
170 print "XPath =", xpath
171 op
= add(op
, 'mark_switch')
172 op
= add(op
, 'xpath', xpath
)
173 op
= add(op
, 'mark_switch')
174 op
= add(op
, 'add_attrib', None, qname
)
175 op
= add(op
, 'put_replace')
177 op
= make_template(op
, child
)
178 op
= add(op
, 'move_left')
179 elif isinstance(child
, ApplyTemplatesElement
):
180 block
= Block(op
.parent
)
181 block
.toggle_restore()
184 sub
= add(sub
, 'mark_switch')
185 # Ugly hack... global *|text() doesn't select in document order
187 sub
= add(sub
, 'do_global', `child
._select
.expression`
)
189 sub
= add(sub
, 'select_children')
190 sub
= add(sub
, 'map', 'XSLT/' + mode_prog_name(child
._mode
))
191 sub
= add(sub
, 'mark_switch')
193 op
.link_to(block
, 'next')
195 elif isinstance(child
, ValueOfElement
):
196 op
= add(op
, 'mark_switch')
197 op
= add(op
, 'xpath', `child
._select
.expression`
)
198 op
= add(op
, 'mark_switch')
199 op
= add(op
, 'put_as_child_end')
200 op
= add(op
, 'move_left')
201 elif isinstance(child
, CopyElement
):
202 op
= add(op
, 'mark_switch')
203 op
= add(op
, 'shallow_yank')
204 op
= add(op
, 'mark_switch')
205 op
= add(op
, 'put_as_child_end')
206 op
= make_template(op
, child
)
207 op
= add(op
, 'move_left')
209 print "Unknown template type", child
, "(%s)" % child
.__class
__